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)
|
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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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`
|
||||||
|
|
|
@ -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
|
- 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
|
|
||||||
|
|
|
@ -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