Testing survey as an extension of geopandas
Kaufmann, 2024.
[1]:
import pandas as pd
import geopandas as gpd
from shapely import Point, LineString
from geometron.survey import TopoPoint, TopoLine, TopoObject
from geometron.geometries import features_to_gdf, gdf_to_features
from geometron.plot import plot_point, plot_line, plot_profile, plot_gdf_survey, plot_basemap
[2]:
@pd.api.extensions.register_dataframe_accessor("survey")
class SurveyAccessor:
def __init__(self, gdf):
self.__required_index = ('id', 'object')
self.__required_fields = {'class': 'object', 'kind': 'object', 'label': 'object', 'show_label': 'boolean', 'geometry': 'geometry'}
self._validate(gdf)
self._gdf = gdf
self._name = ''
self._units = 'm'
def _validate(self, gdf):
# verify that required index and fields are present have the right type
if gdf.index.name!=self.__required_index[0]:
raise IndexError(f'An index named {self.__required_index[0]} is expected for surveys...')
elif gdf.index.dtype!=self.__required_index[1]:
raise IndexError(f'Wrong dtype. The type of index should be {self.__required_index[1]} not {gdf.index.dtype}.')
for col in list(self.__required_fields.keys()):
if col not in gdf.columns:
raise AttributeError(f'Missing {col} in columns...')
elif gdf[col].dtype != self.__required_fields[col]:
raise AttributeError(f'Wrong dtype. The type of column {col} should be {self.__required_fields[col]} not {gdf[col].dtype}.')
self._gdf = gdf.loc[:, list(self.__required_fields.keys())].astype(self.__required_fields)
return self._gdf
def copy(self):
return self._gdf
@property
def name(self):
return self._name
@name.setter
def name(self, value):
assert isinstance(value, str)
self._name = value
def __getitem__(self, item):
f = self._gdf.query(f'index=="{item}"').copy()
return gdf_to_features(f)
def add_features(self, features, **kwargs):
""" Adds features to the current survey
Parameters:
-----------
features: single object with __geo_interface__ | iterable of objects with __geo_interface__
features to add to the survey
Returns:
--------
geopandas.GeoDataFrame
the updated survey
"""
if not hasattr(features, '__iter__'):
features = [features]
gdf = features_to_gdf(features=features, **kwargs)
gdf = pd.concat([self._gdf.survey.copy(), gdf], ignore_index=False)
# copy survey properties
gdf.survey.name = self.name
return gdf
def to_features(self):
pass
def get(self, id):
pass
def plot(self, **kwargs):
ax = plot_gdf_survey(self._gdf, **kwargs)
return ax
[3]:
def create_survey(feature_list=None, **kwargs):
if feature_list is None:
gdf = gpd.GeoDataFrame(columns=['id', 'class', 'kind', 'label', 'show_label', 'geometry'], geometry='geometry')
gdf = gdf.astype({'id': 'object', 'class': 'object', 'kind': 'object', 'label': 'object', 'show_label': 'boolean', 'geometry': 'geometry'})
gdf.set_index('id', inplace=True)
return gdf
elif not hasattr(feature_list, '__iter__'):
feature_list = [feature_list]
print([f.__geo_interface__ for f in feature_list])
gdf = features_to_gdf(feature_list, **kwargs)
return gdf, [f.__geo_interface__ for f in feature_list]
[4]:
L001 = TopoPoint('POINT (0. 0.)', id='L001', kind='landmark')
L002 = TopoPoint('POINT (50. 0.)', id='L002', kind='landmark')
B01 = TopoLine(LineString([L001.geometry, L002.geometry]), id='B01', kind='baseline', show_label=False)
[5]:
B01.__geo_interface__
[5]:
{'type': 'Feature',
'properties': {'id': 'B01',
'class': 'TopoLine',
'kind': 'baseline',
'label': 'B01',
'show_label': False},
'geometry': {'type': 'LineString', 'coordinates': ((0.0, 0.0), (50.0, 0.0))}}
[6]:
s, f_list = create_survey([L001, L002, B01])
[{'type': 'Feature', 'properties': {'id': 'L001', 'class': 'TopoPoint', 'kind': 'landmark', 'label': 'L001', 'show_label': True}, 'geometry': {'type': 'Point', 'coordinates': (0.0, 0.0)}}, {'type': 'Feature', 'properties': {'id': 'L002', 'class': 'TopoPoint', 'kind': 'landmark', 'label': 'L002', 'show_label': True}, 'geometry': {'type': 'Point', 'coordinates': (50.0, 0.0)}}, {'type': 'Feature', 'properties': {'id': 'B01', 'class': 'TopoLine', 'kind': 'baseline', 'label': 'B01', 'show_label': False}, 'geometry': {'type': 'LineString', 'coordinates': ((0.0, 0.0), (50.0, 0.0))}}]
[7]:
s
[7]:
geometry | class | kind | label | show_label | |
---|---|---|---|---|---|
id | |||||
L001 | POINT (0 0) | TopoPoint | landmark | L001 | True |
L002 | POINT (50 0) | TopoPoint | landmark | L002 | True |
B01 | LINESTRING (0 0, 50 0) | TopoLine | baseline | B01 | False |
[8]:
ax = s.survey.plot()
square grid step: 10.0

[9]:
s.survey.name = 'test_survey'
[10]:
u = s.survey.copy()
u.survey.name
[10]:
'test_survey'
[11]:
B01_copy, crs = s.survey['B01']
crs
feature properties: {'class': 'TopoLine', 'kind': 'baseline', 'label': 'B01', 'show_label': False, 'id': 'B01'}
[12]:
B01_copy.show_label=True
B01_copy.plot()

[13]:
s.set_crs('epsg:3857', inplace=True)
[13]:
geometry | class | kind | label | show_label | |
---|---|---|---|---|---|
id | |||||
L001 | POINT (0 0) | TopoPoint | landmark | L001 | True |
L002 | POINT (50 0) | TopoPoint | landmark | L002 | True |
B01 | LINESTRING (0 0, 50 0) | TopoLine | baseline | B01 | False |
[14]:
features, crs = gdf_to_features(s)
features, crs
feature properties: {'class': 'TopoPoint', 'kind': 'landmark', 'label': 'L001', 'show_label': True, 'id': 'L001'}
feature properties: {'class': 'TopoPoint', 'kind': 'landmark', 'label': 'L002', 'show_label': True, 'id': 'L002'}
feature properties: {'class': 'TopoLine', 'kind': 'baseline', 'label': 'B01', 'show_label': False, 'id': 'B01'}
[14]:
([<geometron.survey.survey.TopoPoint at 0x7fc624db8dc0>,
<geometron.survey.survey.TopoPoint at 0x7fc624dc6dd0>,
<geometron.survey.survey.TopoLine at 0x7fc624dc45e0>],
<Projected CRS: EPSG:3857>
Name: WGS 84 / Pseudo-Mercator
Axis Info [cartesian]:
- X[east]: Easting (metre)
- Y[north]: Northing (metre)
Area of Use:
- name: World between 85.06°S and 85.06°N.
- bounds: (-180.0, -85.06, 180.0, 85.06)
Coordinate Operation:
- name: Popular Visualisation Pseudo-Mercator
- method: Popular Visualisation Pseudo Mercator
Datum: World Geodetic System 1984 ensemble
- Ellipsoid: WGS 84
- Prime Meridian: Greenwich)
[15]:
features_to_gdf(features, crs=crs)
[15]:
geometry | class | kind | label | show_label | |
---|---|---|---|---|---|
id | |||||
L001 | POINT (0 0) | TopoPoint | landmark | L001 | True |
L002 | POINT (50 0) | TopoPoint | landmark | L002 | True |
B01 | LINESTRING (0 0, 50 0) | TopoLine | baseline | B01 | False |
[16]:
L003 = TopoPoint('POINT (20. 20.)', id='L003', kind='landmark')
L004 = TopoPoint('POINT (35. 15.)', id='L004', kind='landmark')
B02 = TopoLine(LineString([L003.geometry, L004.geometry]), id='B02', kind='baseline', show_label=False)
[17]:
s = s.survey.add_features([L003, L004])
/home/su530201/.local/share/virtualenvs/geometron-VhFx4lLH/lib/python3.10/site-packages/geopandas/array.py:1638: UserWarning: CRS not set for some of the concatenation inputs. Setting output's CRS as WGS 84 / Pseudo-Mercator (the single non-null crs provided).
return GeometryArray(data, crs=_get_common_crs(to_concat))
[18]:
s.survey.name
[18]:
'test_survey'
[19]:
s.survey.plot()
square grid step: 10.0
[19]:
<Axes: >

[20]:
s.survey.name
[20]:
'test_survey'
[21]:
s = s.survey.add_features(B02, crs='epsg:3857')
[22]:
s.survey.plot()
square grid step: 10.0
[22]:
<Axes: >

[23]:
s.survey['B01']
feature properties: {'class': 'TopoLine', 'kind': 'baseline', 'label': 'B01', 'show_label': False, 'id': 'B01'}
[23]:
(<geometron.survey.survey.TopoLine at 0x7fc62283e860>,
<Projected CRS: EPSG:3857>
Name: WGS 84 / Pseudo-Mercator
Axis Info [cartesian]:
- X[east]: Easting (metre)
- Y[north]: Northing (metre)
Area of Use:
- name: World between 85.06°S and 85.06°N.
- bounds: (-180.0, -85.06, 180.0, 85.06)
Coordinate Operation:
- name: Popular Visualisation Pseudo-Mercator
- method: Popular Visualisation Pseudo Mercator
Datum: World Geodetic System 1984 ensemble
- Ellipsoid: WGS 84
- Prime Meridian: Greenwich)
[24]:
s.query('kind=="baseline"').survey.plot()
square grid step: 10.0
[24]:
<Axes: >

[25]:
ax = s.survey.plot()
square grid step: 10.0

[26]:
L005 = TopoPoint('POINT (30. 30.)', id='L005', kind='landmark')
B03 = TopoLine(LineString([L001.geometry, L005.geometry]), id='B03', kind='baseline', show_label=False)
[27]:
s = s.survey.add_features([L005, B03])
/home/su530201/.local/share/virtualenvs/geometron-VhFx4lLH/lib/python3.10/site-packages/geopandas/array.py:1638: UserWarning: CRS not set for some of the concatenation inputs. Setting output's CRS as WGS 84 / Pseudo-Mercator (the single non-null crs provided).
return GeometryArray(data, crs=_get_common_crs(to_concat))
[28]:
s.survey.plot()
square grid step: 10.0
[28]:
<Axes: >

[29]:
s.query('kind=="landmark"').survey.plot()
square grid step: 10.0
[29]:
<Axes: >
