Implement simplify (using geopandas)

Add preserve_geometry option
This commit is contained in:
phil 2024-01-07 18:30:25 +05:30
parent e3ed311390
commit c3caedea0e
6 changed files with 93 additions and 66 deletions

View file

@ -7,7 +7,8 @@ import logging
from typing import Annotated
from asyncio import CancelledError
from fastapi import (Depends, FastAPI, HTTPException, Response, Header, WebSocket, WebSocketDisconnect,
from fastapi import (Depends, FastAPI, HTTPException, Response, Header,
WebSocket, WebSocketDisconnect,
status, responses)
from gisaf.models.authentication import User
@ -76,7 +77,8 @@ async def live_layer(store: str, websocket: WebSocket):
async def get_geojson(store_name,
user: User = Depends(get_current_active_user),
If_None_Match: Annotated[str | None, Header()] = None,
simplify: Annotated[float | None, Header()] = 50.0,
simplify: Annotated[float | None, Header()] = None,
preserveTopology: Annotated[bool|None, Header()] = None,
):
"""
Some REST stores coded manually (route prefixed with "gj": geojson).
@ -88,45 +90,45 @@ async def get_geojson(store_name,
model = registry.stores.loc[store_name].model
except KeyError:
raise HTTPException(status.HTTP_404_NOT_FOUND)
if hasattr(model, 'viewable_role'):
if not(user and user.can_view(model)):
logger.info(f'{user.username if user else "Anonymous"} tried to access {model}')
username = user.username if user else "Anonymous"
logger.info(f'{username} tried to access {model}')
raise HTTPException(status.HTTP_401_UNAUTHORIZED)
if await redis_store.has_channel(store_name):
## Live layers
data = await redis_store.get_layer_as_json(store_name)
return Response(content=data.decode(),
media_type="application/json")
# elif not model:
# raise HTTPException(status.HTTP_404_NOT_FOUND)
if model.cache_enabled:
ttag = await redis_store.get_ttag(store_name)
if ttag and If_None_Match == ttag:
return status.HTTP_304_NOT_MODIFIED
if hasattr(model, 'get_geojson'):
geojson = await model.get_geojson(simplify_tolerance=simplify, registry=registry)
geojson = await model.get_geojson(simplify_tolerance=simplify,
preserve_topology=preserveTopology,
registry=registry)
## Store to redis for caching
if use_cache:
await redis_store.store_json(model, geojson)
resp = geojson
elif model.can_get_features_as_df:
## Get the GeoDataframe (gdf) with GeoPandas
## get_popup and get_propertites get the gdf as argument and can use vectorised operations
## get_popup and get_propertites get the gdf as argument
## and can use vectorised operations
try:
gdf = await model.get_geo_df(cast=True, with_related=True, filter_columns=True)
gdf = await model.get_gdf(cast=True, with_related=True,
# filter_columns=True,
preserve_topology=preserveTopology,
simplify_tolerance=simplify)
except CancelledError as err:
logger.debug(f'Request for {store_name} cancelled while getting gdf')
logger.debug(f'Getting {store_name} cancelled while getting gdf')
raise err
except Exception as err:
logger.exception(err)
raise err #status.HTTP_500_INTERNAL_SERVER_ERROR
## The query of category defined models gets the status (not sure how and this could be skipped)
raise HTTPException(status.HTTP_500_INTERNAL_SERVER_ERROR)
## The query of category defined models gets the status
## (not sure how and this could be skipped)
## Other models do not have: just add it manually from the model itself
if 'status' not in gdf.columns:
gdf['status'] = model.status
@ -138,35 +140,38 @@ async def get_geojson(store_name,
for property, values in properties.items():
columns.append(property)
gdf[property] = values
geojson = gdf[columns].to_json(separators=(',', ':'), check_circular=False)
geojson = gdf[columns].to_json(separators=(',', ':'),
check_circular=False)
## Store to redis for caching
if use_cache:
await redis_store.store_json(model, geojson)
resp = geojson
else:
logger.warn(f"{model} doesn't allow using dataframe for generating json!")
attrs, features_kwargs = await model.get_features_attrs(simplify)
## Using gino: allows OO model (get_info, etc)
try:
attrs['features'] = await model.get_features_in_bulk_gino(**features_kwargs)
except Exception as err:
logger.exception(err)
raise status.HTTP_500_INTERNAL_SERVER_ERROR
resp = attrs
raise HTTPException(status.HTTP_500_INTERNAL_SERVER_ERROR,
detail='Gino is for: Gino Is No Option')
# logger.warn(f"{model} doesn't allow using dataframe for generating json!")
# attrs, features_kwargs = await model.get_features_attrs(simplify)
# ## Using gino: allows OO model (get_info, etc)
# try:
# attrs['features'] = await model.get_features_in_bulk_gino(**features_kwargs)
# except Exception as err:
# logger.exception(err)
# raise status.HTTP_500_INTERNAL_SERVER_ERROR
# resp = attrs
headers = {}
if model.cache_enabled and ttag:
headers['ETag'] = ttag
return Response(content=resp, media_type="application/json", headers=headers)
return Response(content=resp,
media_type="application/json", headers=headers)
@api.get('/gj/{store_name}/popup/{id}')
async def gj_popup(store_name: str, id: int):
model = registry.geom.get(store_name)
if not hasattr(model, 'get_popup_dynamic'):
return ''
obj = await model.get(id)
## Escape characters for json
popup_more = obj.get_popup_dynamic().replace('"', '\\"').replace('\n', '\\n')
return {"text": popup_more}
# @api.get('/gj/{store_name}/popup/{id}')
# async def gj_popup(store_name: str, id: int):
# model = registry.geom.get(store_name)
# if not hasattr(model, 'get_popup_dynamic'):
# return ''
# obj = await model.get(id)
# ## Escape characters for json
# popup_more = obj.get_popup_dynamic().replace('"', '\\"').replace('\n', '\\n')
# return {"text": popup_more}