test openlayers, adjust geofeature models for openlayers
This commit is contained in:
parent
328d2e8684
commit
b476272808
|
@ -1,4 +1,3 @@
|
|||
Django backend, vue.js frontend
|
||||
Tagging support (tag roads, trails, etc)
|
||||
Sharing (share individual items or select items or tags to include)
|
||||
Organization by folder
|
||||
|
|
|
@ -47,7 +47,7 @@ def process_feature(converted_kml) -> Tuple[list, ImportLog]:
|
|||
for i, timestamp_str in enumerate(feature['properties']['times']):
|
||||
timestamp = int(parse(timestamp_str).timestamp() * 1000)
|
||||
feature['geometry']['coordinates'][i].append(timestamp)
|
||||
feature['properties'] = GeojsonRawProperty(**feature['properties']).dict()
|
||||
feature['properties'] = GeojsonRawProperty(**feature['properties']).model_dump()
|
||||
features.append(feature)
|
||||
else:
|
||||
# Log the error
|
||||
|
@ -78,5 +78,4 @@ def load_geojson_type(data: dict) -> dict:
|
|||
'coordinates': item.pop('coordinates'),
|
||||
}
|
||||
item['type'] = 'Feature'
|
||||
item['properties']['title'] = item['properties'].pop('name')
|
||||
return geojson_dict
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
from datetime import datetime
|
||||
import json
|
||||
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 Field, BaseModel
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
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):
|
||||
|
@ -15,42 +14,54 @@ class GeoFeatureType(str, Enum):
|
|||
POLYGON = 'Polygon'
|
||||
|
||||
|
||||
class GeoFeatureProperties(BaseModel):
|
||||
tags: List[str] = Field(default_factory=list)
|
||||
created: datetime = datetime.utcnow().replace(tzinfo=pytz.utc)
|
||||
software: str = Field(SOFTWARE_NAME, frozen=True)
|
||||
software_version: str = Field(SOFTWARE_VERSION, frozen=True)
|
||||
class Rendering(BaseModel):
|
||||
stroke_width: int = Field(2, alias='strokeWidth')
|
||||
stroke_color: Tuple[int, int, int, float] = Field((255, 0, 0, 1.0), alias='strokeColor')
|
||||
fill_color: Optional[Tuple[int, int, int, float]] = Field((255, 0, 0, 0.2), alias='fillColor')
|
||||
|
||||
|
||||
class GeoFeature(BaseModel):
|
||||
"""
|
||||
A thing that's shown on the map.
|
||||
Can be a point, linestring, or polygon.
|
||||
"""
|
||||
class Properties(BaseModel):
|
||||
name: str
|
||||
id: int # From the database
|
||||
type: GeoFeatureType
|
||||
id: Optional[int] = -1
|
||||
description: Optional[str] = None
|
||||
geometry: List
|
||||
properties: GeoFeatureProperties = Field(default_factory=GeoFeatureProperties)
|
||||
tags: Optional[List[str]] = Field(default_factory=list)
|
||||
rendering: Optional[Rendering] = Field(default_factory=Rendering)
|
||||
|
||||
|
||||
class GeoPoint(GeoFeature):
|
||||
class PointFeatureGeometry(BaseModel):
|
||||
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
|
||||
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
|
||||
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]:
|
||||
|
@ -59,20 +70,32 @@ def geojson_to_geofeature(geojson: dict) -> Tuple[List[GeoFeatureSupported], Imp
|
|||
for item in geojson['features']:
|
||||
match item['geometry']['type'].lower():
|
||||
case 'point':
|
||||
c = GeoPoint
|
||||
c = PointFeature
|
||||
case 'linestring':
|
||||
c = GeoLineString
|
||||
c = LineStringFeature
|
||||
case 'polygon':
|
||||
c = GeoPolygon
|
||||
c = PolygonFeature
|
||||
case _:
|
||||
import_log.add(f'Feature named "{item["properties"]["title"]}" had unsupported type "{item["geometry"]["type"]}".')
|
||||
continue
|
||||
result.append(c(
|
||||
name=item['properties']['title'],
|
||||
id=-1, # This will be updated after it's added to the main data store.
|
||||
description=item['properties']['description'],
|
||||
tags=item['properties']['feature_tags'],
|
||||
geometry=item['geometry']['coordinates']
|
||||
))
|
||||
|
||||
f = c(**item)
|
||||
if isinstance(f, (PointFeature, LineStringFeature)):
|
||||
del f.properties.rendering.fill_color
|
||||
|
||||
# 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
|
||||
|
||||
|
||||
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)
|
||||
|
|
|
@ -7,4 +7,4 @@ class GeojsonRawProperty(BaseModel):
|
|||
# A class to whitelist these properties.
|
||||
name: str
|
||||
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`
|
||||
|
|
|
@ -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))
|
|
@ -1,7 +1 @@
|
|||
1. Style main import page
|
||||
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
|
|
@ -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>
|
Loading…
Reference in New Issue