test openlayers, adjust geofeature models for openlayers

This commit is contained in:
Cyberes 2024-09-29 20:52:36 -06:00
parent 328d2e8684
commit b476272808
7 changed files with 166 additions and 47 deletions

View File

@ -1,4 +1,3 @@
Django backend, vue.js frontend
Tagging support (tag roads, trails, etc) Tagging support (tag roads, trails, etc)
Sharing (share individual items or select items or tags to include) Sharing (share individual items or select items or tags to include)
Organization by folder Organization by folder

View File

@ -47,7 +47,7 @@ def process_feature(converted_kml) -> Tuple[list, ImportLog]:
for i, timestamp_str in enumerate(feature['properties']['times']): for i, timestamp_str in enumerate(feature['properties']['times']):
timestamp = int(parse(timestamp_str).timestamp() * 1000) timestamp = int(parse(timestamp_str).timestamp() * 1000)
feature['geometry']['coordinates'][i].append(timestamp) feature['geometry']['coordinates'][i].append(timestamp)
feature['properties'] = GeojsonRawProperty(**feature['properties']).dict() feature['properties'] = GeojsonRawProperty(**feature['properties']).model_dump()
features.append(feature) features.append(feature)
else: else:
# Log the error # Log the error
@ -78,5 +78,4 @@ def load_geojson_type(data: dict) -> dict:
'coordinates': item.pop('coordinates'), 'coordinates': item.pop('coordinates'),
} }
item['type'] = 'Feature' item['type'] = 'Feature'
item['properties']['title'] = item['properties'].pop('name')
return geojson_dict return geojson_dict

View File

@ -1,12 +1,11 @@
from datetime import datetime import json
from enum import Enum from enum import Enum
from typing import Optional, List, Union, Tuple from typing import List, Tuple, Optional
from typing import Union
import pytz from pydantic import BaseModel, Field
from pydantic import Field, BaseModel
from geo_lib.daemon.workers.workers_lib.importer.logging import ImportLog from geo_lib.daemon.workers.workers_lib.importer.logging import ImportLog
from geo_lib.geo_backend import SOFTWARE_NAME, SOFTWARE_VERSION
class GeoFeatureType(str, Enum): class GeoFeatureType(str, Enum):
@ -15,42 +14,54 @@ class GeoFeatureType(str, Enum):
POLYGON = 'Polygon' POLYGON = 'Polygon'
class GeoFeatureProperties(BaseModel): class Rendering(BaseModel):
tags: List[str] = Field(default_factory=list) stroke_width: int = Field(2, alias='strokeWidth')
created: datetime = datetime.utcnow().replace(tzinfo=pytz.utc) stroke_color: Tuple[int, int, int, float] = Field((255, 0, 0, 1.0), alias='strokeColor')
software: str = Field(SOFTWARE_NAME, frozen=True) fill_color: Optional[Tuple[int, int, int, float]] = Field((255, 0, 0, 0.2), alias='fillColor')
software_version: str = Field(SOFTWARE_VERSION, frozen=True)
class GeoFeature(BaseModel): class Properties(BaseModel):
"""
A thing that's shown on the map.
Can be a point, linestring, or polygon.
"""
name: str name: str
id: int # From the database id: Optional[int] = -1
type: GeoFeatureType
description: Optional[str] = None description: Optional[str] = None
geometry: List tags: Optional[List[str]] = Field(default_factory=list)
properties: GeoFeatureProperties = Field(default_factory=GeoFeatureProperties) rendering: Optional[Rendering] = Field(default_factory=Rendering)
class GeoPoint(GeoFeature): class PointFeatureGeometry(BaseModel):
type: GeoFeatureType = GeoFeatureType.POINT type: GeoFeatureType = GeoFeatureType.POINT
geometry: List[float] coordinates: Union[Tuple[float, float], Tuple[float, float, float]]
class GeoLineString(GeoFeature): class LineStringGeometry(BaseModel):
type: GeoFeatureType = GeoFeatureType.LINESTRING type: GeoFeatureType = GeoFeatureType.LINESTRING
geometry: List[List[float]] coordinates: List[Union[Tuple[float, float], Tuple[float, float, float], Tuple[float, float, float, int]]]
class GeoPolygon(GeoFeature): class PolygonGeometry(BaseModel):
type: GeoFeatureType = GeoFeatureType.POLYGON type: GeoFeatureType = GeoFeatureType.POLYGON
geometry: List[List[List[float]]] coordinates: List[List[Union[Tuple[float, float], Tuple[float, float, float]]]]
GeoFeatureSupported = Union[GeoPoint, GeoLineString, GeoPolygon] class Feature(BaseModel):
type: str = 'Feature'
geometry: Union[PointFeatureGeometry, LineStringGeometry, PolygonGeometry]
properties: Properties
class PointFeature(Feature):
geometry: PointFeatureGeometry
class LineStringFeature(Feature):
geometry: LineStringGeometry
class PolygonFeature(Feature):
geometry: PolygonGeometry
GeoFeatureSupported = Union[PointFeature, LineStringFeature, PolygonFeature]
def geojson_to_geofeature(geojson: dict) -> Tuple[List[GeoFeatureSupported], ImportLog]: def geojson_to_geofeature(geojson: dict) -> Tuple[List[GeoFeatureSupported], ImportLog]:
@ -59,20 +70,32 @@ def geojson_to_geofeature(geojson: dict) -> Tuple[List[GeoFeatureSupported], Imp
for item in geojson['features']: for item in geojson['features']:
match item['geometry']['type'].lower(): match item['geometry']['type'].lower():
case 'point': case 'point':
c = GeoPoint c = PointFeature
case 'linestring': case 'linestring':
c = GeoLineString c = LineStringFeature
case 'polygon': case 'polygon':
c = GeoPolygon c = PolygonFeature
case _: case _:
import_log.add(f'Feature named "{item["properties"]["title"]}" had unsupported type "{item["geometry"]["type"]}".') import_log.add(f'Feature named "{item["properties"]["title"]}" had unsupported type "{item["geometry"]["type"]}".')
continue continue
result.append(c(
name=item['properties']['title'], f = c(**item)
id=-1, # This will be updated after it's added to the main data store. if isinstance(f, (PointFeature, LineStringFeature)):
description=item['properties']['description'], del f.properties.rendering.fill_color
tags=item['properties']['feature_tags'],
geometry=item['geometry']['coordinates'] # TODO: do this shit
)) f.properties.id = -1 # This will be updated after it's added to the main data store.
result.append(f)
return result, import_log return result, import_log
def geofeature_to_geojson(feature: Union[GeoFeatureSupported, list]) -> str:
if isinstance(feature, list):
return json.dumps({
'type': 'FeatureCollection',
'features': [json.loads(x.model_dump_json(by_alias=True)) for x in feature]
})
else:
return feature.model_dump_json(by_alias=True)

View File

@ -7,4 +7,4 @@ class GeojsonRawProperty(BaseModel):
# A class to whitelist these properties. # A class to whitelist these properties.
name: str name: str
description: Optional[str] = None description: Optional[str] = None
feature_tags: List[str] = Field(default_factory=list) tags: List[str] = Field(default_factory=list, alias='feature_tags') # kml2geojson calls this field `feature_tags`

View File

@ -0,0 +1,19 @@
import argparse
import sys
from pathlib import Path
sys.path.append(str(list(Path(__file__).parents)[1]))
from geo_lib.daemon.workers.workers_lib.importer.kml import kml_to_geojson
from geo_lib.types.feature import geojson_to_geofeature, geofeature_to_geojson
parser = argparse.ArgumentParser()
parser.add_argument('kml_path')
args = parser.parse_args()
raw_kml = Path(args.kml_path).expanduser().absolute().resolve().read_text()
geojson_data, kml_conv_messages = kml_to_geojson(raw_kml)
geofetures, typing_messages = geojson_to_geofeature(geojson_data)
print(geofeature_to_geojson(geofetures))

View File

@ -1,7 +1 @@
1. Style main import page - For tracks, set the created date to the timestamp of the first point in the track
2. Fix created field reset on edit imported
3. Implement refresh on edit imported
4. Style messages/log on edit imported
5. Implement upload working animation on edit imported
- For tracks, set the created date to the timestamp of the first point in the track

View File

@ -0,0 +1,85 @@
<!DOCTYPE html>
<html style="width: 100%; height: 100%;">
<head>
<title>OpenLayers LineString Example</title>
<link href="https://openlayers.org/en/v6.5.0/css/ol.css" rel="stylesheet" type="text/css">
<script src="https://openlayers.org/en/v6.5.0/build/ol.js"></script>
</head>
<body style="width: 100%; height: 100%; margin:0">
<div id="map" style="width: 100%; height: 100%;"></div>
<script>
const geojsonData = {
"type": "FeatureCollection",
"features": []
};
const features = new ol.format.GeoJSON().readFeatures(geojsonData, {
featureProjection: 'EPSG:3857',
dataProjection: 'EPSG:4326'
});
const vectorSource = new ol.source.Vector({
features: features
});
const vectorLayer = new ol.layer.Vector({
source: vectorSource,
style: function (feature) {
const rendering = feature.get('rendering');
const geometryType = feature.getGeometry().getType();
if (geometryType === 'Point') {
return new ol.style.Style({
image: new ol.style.Circle({
radius: 6,
fill: new ol.style.Fill({
color: rendering.strokeColor
}),
stroke: new ol.style.Stroke({
color: rendering.strokeColor,
width: rendering.strokeWidth
})
})
});
} else if (geometryType === 'LineString') {
return new ol.style.Style({
stroke: new ol.style.Stroke({
color: rendering.strokeColor,
width: rendering.strokeWidth
})
});
} else if (geometryType === 'Polygon') {
return new ol.style.Style({
stroke: new ol.style.Stroke({
color: rendering.strokeColor,
width: rendering.strokeWidth
}),
fill: new ol.style.Fill({
color: rendering.fillColor
})
});
}
}
});
const map = new ol.Map({
target: 'map',
layers: [
new ol.layer.Tile({
source: new ol.source.OSM()
}),
vectorLayer
],
view: new ol.View({
center: ol.proj.fromLonLat([-104.692626, 38.881215]),
zoom: 10
})
});
</script>
<style>
.ol-attribution {
display: none;
}
</style>
</body>
</html>