feature-info: migrate to pydantic, fix live
This commit is contained in:
parent
71cb491617
commit
e3ed311390
5 changed files with 203 additions and 172 deletions
|
@ -39,6 +39,7 @@ from gisaf.models.models_base import Model
|
|||
from gisaf.models.metadata import gisaf_survey, gisaf_admin, survey
|
||||
from gisaf.models.misc import Qml
|
||||
from gisaf.models.category import Category
|
||||
from gisaf.models.to_migrate import InfoItem
|
||||
# from gisaf.models.survey import Equipment, Surveyor, Accuracy
|
||||
# from gisaf.models.project import Project
|
||||
|
||||
|
@ -112,27 +113,35 @@ class BaseSurveyModel(BaseModel):
|
|||
# info = await super(BaseSurveyModel, self).get_geo_info()
|
||||
# return info
|
||||
|
||||
async def get_survey_info(self):
|
||||
async def get_survey_info(self) -> list[InfoItem]:
|
||||
info = await super(BaseSurveyModel, self).get_survey_info()
|
||||
if self.category:
|
||||
info['ISO layer name'] = self.iso_layer_name
|
||||
info['survey category'] = '{} ({})'.format(self.category.description, self.category.name)
|
||||
info.append(InfoItem(key='ISO layer name',
|
||||
value=self.iso_layer_name))
|
||||
info.append(InfoItem(key='survey category',
|
||||
value=f'{self.category.description} ({self.category.name})'))
|
||||
if self.project_id:
|
||||
info['project'] = self.project.name
|
||||
info.append(InfoItem(key='project',
|
||||
value=self.project.name))
|
||||
if self.srvyr_id:
|
||||
info['surveyor'] = self.surveyor.name
|
||||
info.append(InfoItem(key='surveyor',
|
||||
value=self.surveyor.name))
|
||||
if self.equip_id:
|
||||
info['survey equipment'] = self.equipment.name
|
||||
info.append(InfoItem(key='survey equipment',
|
||||
value=self.equipment.name))
|
||||
if self.accur_id:
|
||||
info['survey accuracy'] = self.accuracy.name
|
||||
info.append(InfoItem(key='survey accuracy',
|
||||
value=self.accuracy.name))
|
||||
if self.date:
|
||||
info['survey date'] = self.date.strftime(LOCALE_DATE_FORMAT)
|
||||
info.append(InfoItem(key='survey date',
|
||||
value=self.date.strftime(LOCALE_DATE_FORMAT)))
|
||||
if self.orig_id:
|
||||
info['original id'] = self.orig_id
|
||||
info.append(InfoItem(key='original id',
|
||||
value=self.orig_id))
|
||||
return info
|
||||
|
||||
@property
|
||||
def iso_layer_name(self):
|
||||
def iso_layer_name(self) -> str:
|
||||
"""
|
||||
The ISO layer name, built on the category and status
|
||||
"""
|
||||
|
@ -358,17 +367,17 @@ class GeoModelNoStatus(Model):
|
|||
def __str__(self):
|
||||
return self.caption
|
||||
|
||||
async def get_geo_info(self):
|
||||
async def get_geo_info(self) -> list[InfoItem]:
|
||||
"""
|
||||
Geographical info
|
||||
"""
|
||||
return {}
|
||||
return []
|
||||
|
||||
async def get_survey_info(self):
|
||||
async def get_survey_info(self) -> list[InfoItem]:
|
||||
"""
|
||||
Quality info: project, source, accuracy...
|
||||
"""
|
||||
return OrderedDict()
|
||||
return []
|
||||
|
||||
async def get_info(self) -> dict[str, str]:
|
||||
"""
|
||||
|
@ -420,13 +429,18 @@ class GeoModelNoStatus(Model):
|
|||
async def get_properties(cls, df):
|
||||
return {}
|
||||
|
||||
async def get_tags(self):
|
||||
async def get_tags(self) -> list[InfoItem]:
|
||||
from gisaf.models.tags import Tags
|
||||
async with db_session() as session:
|
||||
query = select(Tags.tags).where(Tags.store == self.get_store_name(),
|
||||
Tags.ref_id == self.id)
|
||||
tags = await session.exec(query)
|
||||
return tags.one_or_none() or {}
|
||||
data = await session.exec(query)
|
||||
tags = data.one_or_none()
|
||||
if tags is not None:
|
||||
return [InfoItem(key=key, value=value)
|
||||
for key, value in tags.items()]
|
||||
else:
|
||||
return []
|
||||
|
||||
@cached_property
|
||||
def shapely_geom(self):
|
||||
|
@ -453,7 +467,8 @@ class GeoModelNoStatus(Model):
|
|||
"""
|
||||
return ''
|
||||
|
||||
async def get_feature_as_dict(self, simplify_tolerance=None, reproject=False, css_class_prefix=''):
|
||||
async def get_feature_as_dict(self, simplify_tolerance=None,
|
||||
reproject=False, css_class_prefix=''):
|
||||
"""
|
||||
Get the parameters of this object (feature)
|
||||
:param css_class_prefix: for leaflet only
|
||||
|
@ -694,7 +709,8 @@ class GeoModelNoStatus(Model):
|
|||
|
||||
@classmethod
|
||||
async def get_geo_df(cls, where=None, crs=None, reproject=False,
|
||||
filter_columns=False, with_popup=False, **kwargs) -> gpd.GeoDataFrame:
|
||||
filter_columns=False, with_popup=False,
|
||||
**kwargs) -> gpd.GeoDataFrame:
|
||||
"""
|
||||
Return a GeoPandas GeoDataFrame of all records
|
||||
:param where: where clause for the query (eg. Model.attr=='foo')
|
||||
|
@ -827,38 +843,42 @@ class GeoPointModelNoStatus(GeoModelNoStatus):
|
|||
|
||||
return writer
|
||||
|
||||
async def get_geo_info(self):
|
||||
info = OrderedDict()
|
||||
if self.shapely_geom:
|
||||
info['longitude'] = '{:.6f}'.format(self.shapely_geom.x)
|
||||
info['latitude'] = '{:.6f}'.format(self.shapely_geom.y)
|
||||
return info
|
||||
async def get_geo_info(self) -> list[InfoItem]:
|
||||
return [
|
||||
InfoItem(key='longitude', value='{:.6f}.format(self.geom.x)'),
|
||||
InfoItem(key='latitude', value='{:.6f}.format(self.geom.y)'),
|
||||
]
|
||||
|
||||
class GeoPointModel(GeoPointModelNoStatus, GeoModel):
|
||||
...
|
||||
|
||||
|
||||
class GeoPointZModel(GeoPointModel):
|
||||
geom: Annotated[str, WKBElement] = Field(sa_type=Geometry('POINTZ', dimension=3, srid=conf.geo.srid))
|
||||
geom: Annotated[str, WKBElement] = Field(
|
||||
sa_type=Geometry('POINTZ', dimension=3, srid=conf.geo.srid))
|
||||
shapefile_model: ClassVar[int] = POINTZ
|
||||
|
||||
def get_coords(self):
|
||||
return (self.shapely_geom.x, self.shapely_geom.y, self.shapely_geom.z)
|
||||
|
||||
async def get_geo_info(self):
|
||||
async def get_geo_info(self) -> list[InfoItem]:
|
||||
info = await super(GeoPointZModel, self).get_geo_info()
|
||||
info['elevation (m)'] = '{:.2f}'.format(self.shapely_geom.z)
|
||||
info.append(
|
||||
InfoItem(key='elevation (m)', value='{:.2f}'.format(self.shapely_geom.z))
|
||||
)
|
||||
return info
|
||||
|
||||
|
||||
class GeoPointMModel(GeoPointZModel):
|
||||
shapefile_model: ClassVar[int] = POINTZ
|
||||
geom: Annotated[str, WKBElement] = Field(sa_type=Geometry('POINTZ', dimension=3, srid=conf.geo.srid))
|
||||
geom: Annotated[str, WKBElement] = Field(
|
||||
sa_type=Geometry('POINTZ', dimension=3, srid=conf.geo.srid))
|
||||
|
||||
|
||||
class GeoLineModel(GeoModel):
|
||||
shapefile_model: ClassVar[int] = POLYLINE
|
||||
geom: Annotated[str, WKBElement] = Field(sa_type=Geometry('LINESTRING', srid=conf.geo.srid))
|
||||
geom: Annotated[str, WKBElement] = Field(
|
||||
sa_type=Geometry('LINESTRING', srid=conf.geo.srid))
|
||||
mapbox_type: ClassVar[str] = 'line'
|
||||
base_gis_type: ClassVar[str] = 'Line'
|
||||
|
||||
|
@ -910,34 +930,38 @@ class GeoLineModel(GeoModel):
|
|||
points = wkb.loads(self.geom.data)
|
||||
return zip(points.coords.xy[0], points.coords.xy[1])
|
||||
|
||||
async def get_geo_info(self):
|
||||
info = OrderedDict()
|
||||
async def get_geo_info(self) -> list[InfoItem]:
|
||||
bounds = self.shapely_geom.bounds
|
||||
info['longitude'] = '{:.6f} - {:.6f}'.format(bounds[0], bounds[2])
|
||||
info['latitude'] = '{:.6f} - {:.6f}'.format(bounds[1], bounds[3])
|
||||
info['length (m)'] = '{self.length:.2f}'.format(self=self)
|
||||
return info
|
||||
return [
|
||||
InfoItem(key='longitude', value='{:.6f} - {:.6f}'.format(bounds[0], bounds[2])),
|
||||
InfoItem(key='latitude', value='{:.6f} - {:.6f}'.format(bounds[1], bounds[3])),
|
||||
InfoItem(key='length (m)', value='{self.length:.2f}'.format(self=self))
|
||||
]
|
||||
|
||||
|
||||
class GeoLineModelZ(GeoLineModel):
|
||||
shapefile_model: ClassVar[int] = POLYLINEZ
|
||||
geom: Annotated[str, WKBElement] = Field(sa_type=Geometry('LINESTRINGZ', dimension=3, srid=conf.geo.srid))
|
||||
geom: Annotated[str, WKBElement] = Field(
|
||||
sa_type=Geometry('LINESTRINGZ', dimension=3, srid=conf.geo.srid))
|
||||
|
||||
async def get_geo_info(self):
|
||||
async def get_geo_info(self) -> list[InfoItem]:
|
||||
info = await super(GeoLineModelZ, self).get_geo_info()
|
||||
elevations = [cc[2] for cc in self.shapely_geom.coords]
|
||||
elev_min = min(elevations)
|
||||
elev_max = max(elevations)
|
||||
if elev_min == elev_max:
|
||||
info['elevation (m)'] = '{:.2f}'.format(elev_min)
|
||||
info.append(InfoItem(key='elevation (m)',
|
||||
value='{:.2f}'.format(elev_min)))
|
||||
else:
|
||||
info['elevation (m)'] = '{:.2f} - {:.2f}'.format(elev_min, elev_max)
|
||||
info.append(InfoItem(key='elevation (m)',
|
||||
value='{:.2f} - {:.2f}'.format(elev_min, elev_max)))
|
||||
return info
|
||||
|
||||
|
||||
class GeoPolygonModel(GeoModel):
|
||||
shapefile_model: ClassVar[int] = POLYGON
|
||||
geom: Annotated[str, WKBElement] = Field(sa_type=Geometry('POLYGON', srid=conf.geo.srid))
|
||||
geom: Annotated[str, WKBElement] = Field(
|
||||
sa_type=Geometry('POLYGON', srid=conf.geo.srid))
|
||||
mapbox_type: ClassVar[str] = 'fill'
|
||||
base_gis_type: ClassVar[str] = 'Polygon'
|
||||
|
||||
|
@ -993,24 +1017,31 @@ class GeoPolygonModel(GeoModel):
|
|||
points = wkb.loads(self.geom.data)
|
||||
return zip(points.exterior.coords.xy[0], points.exterior.coords.xy[1])
|
||||
|
||||
async def get_geo_info(self):
|
||||
info = OrderedDict()
|
||||
async def get_geo_info(self) -> list[InfoItem]:
|
||||
info = []
|
||||
area = self.area
|
||||
bounds = self.shapely_geom.bounds
|
||||
info['longitude'] = '{:.6f} - {:.6f}'.format(bounds[0], bounds[2])
|
||||
info['latitude'] = '{:.6f} - {:.6f}'.format(bounds[1], bounds[3])
|
||||
info['length (m)'] = '{:.2f}'.format(self.length)
|
||||
info['area (sq. m)'] = '{:.1f} sq. m'.format(area)
|
||||
info['area (ha)'] = '{:.1f} ha'.format(area / 10000)
|
||||
info['area (acre)'] = '{:.1f} acres'.format(area / 4046.85643005078874)
|
||||
info.append(InfoItem(key='longitude',
|
||||
value='{:.6f} - {:.6f}'.format(bounds[0], bounds[2])))
|
||||
info.append(InfoItem(key='latitude',
|
||||
value='{:.6f} - {:.6f}'.format(bounds[1], bounds[3])))
|
||||
info.append(InfoItem(key='length (m)',
|
||||
value='{:.2f}'.format(self.length)))
|
||||
info.append(InfoItem(key='area (sq. m)',
|
||||
value='{:.1f} sq. m'.format(area)))
|
||||
info.append(InfoItem(key='area (ha)',
|
||||
value='{:.1f} ha'.format(area / 10000)))
|
||||
info.append(InfoItem(key='area (acre)',
|
||||
value='{:.1f} acres'.format(area / 4046.85643005078874)))
|
||||
return info
|
||||
|
||||
|
||||
class GeoPolygonModelZ(GeoPolygonModel):
|
||||
shapefile_model: ClassVar[int] = POLYGONZ
|
||||
geom: Annotated[str, WKBElement] = Field(sa_type=Geometry('POLYGONZ', dimension=3, srid=conf.geo.srid))
|
||||
geom: Annotated[str, WKBElement] = Field(
|
||||
sa_type=Geometry('POLYGONZ', dimension=3, srid=conf.geo.srid))
|
||||
|
||||
async def get_geo_info(self):
|
||||
async def get_geo_info(self) -> list[InfoItem]:
|
||||
info = await super(GeoPolygonModelZ, self).get_geo_info()
|
||||
if hasattr(self.shapely_geom, 'exterior'):
|
||||
coords = self.shapely_geom.exterior.coords
|
||||
|
@ -1020,9 +1051,11 @@ class GeoPolygonModelZ(GeoPolygonModel):
|
|||
elev_min = min(elevations)
|
||||
elev_max = max(elevations)
|
||||
if elev_min == elev_max:
|
||||
info['elevation (m)'] = '{:.2f}'.format(elev_min)
|
||||
info.append(InfoItem(key='elevation (m)',
|
||||
value='{:.2f}'.format(elev_min)))
|
||||
else:
|
||||
info['elevation (m)'] = '{:.2f} - {:.2f}'.format(elev_min, elev_max)
|
||||
info.append(InfoItem(key='elevation (m)',
|
||||
value='{:.2f} - {:.2f}'.format(elev_min, elev_max)))
|
||||
return info
|
||||
|
||||
|
||||
|
@ -1038,7 +1071,8 @@ class LineWorkSurveyModel(SurveyModel):
|
|||
def match_raw_points(self):
|
||||
reprojected_geom = transform(reproject_func, self.shapely_geom)
|
||||
reprojected_geom_geoalchemy = from_shape(reprojected_geom, conf.raw_survey_srid)
|
||||
raw_survey_points_project = self.raw_model.query.filter(self.raw_model.project_id==self.project_id)
|
||||
raw_survey_points_project = self.raw_model.query.filter(
|
||||
self.raw_model.project_id==self.project_id)
|
||||
query = raw_survey_points_project.filter(
|
||||
func.ST_Distance(reprojected_geom_geoalchemy, self.raw_model.geom) < conf.epsilon
|
||||
)
|
||||
|
|
|
@ -28,7 +28,7 @@ class DataProvider(BaseModel):
|
|||
|
||||
class InfoItem(BaseModel):
|
||||
key: str
|
||||
value: str
|
||||
value: str | float | int
|
||||
|
||||
|
||||
class InfoCategory(BaseModel):
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue