mirror of
https://github.com/umap-project/umap.git
synced 2025-04-29 11:52:38 +02:00
fix(sync): Import the data when syncing GeoJSON objects.
Prior to these changes, the data wasn't transmitted over WebSocket, and even if present it wasn't taken into account.
This commit is contained in:
parent
e24173eb9f
commit
ce0f3c9d3e
8 changed files with 63 additions and 32 deletions
|
@ -16,7 +16,7 @@ export class SyncEngine {
|
|||
}
|
||||
_initialize() {
|
||||
this.transport = undefined
|
||||
const noop = () => undefined
|
||||
const noop = () => {}
|
||||
// by default, all operations do nothing, until the engine is started.
|
||||
this.upsert = this.update = this.delete = noop
|
||||
}
|
||||
|
|
|
@ -68,25 +68,29 @@ class FeatureUpdater extends BaseUpdater {
|
|||
return datalayer.getFeatureById(id)
|
||||
}
|
||||
|
||||
// XXX Not sure about the naming. It's returning latlng OR latlngS
|
||||
getGeometry({ type, coordinates }) {
|
||||
if (type == 'Point') {
|
||||
return L.GeoJSON.coordsToLatLng(coordinates)
|
||||
}
|
||||
return L.GeoJSON.coordsToLatLngs(coordinates)
|
||||
}
|
||||
|
||||
// Create or update an object at a specific position
|
||||
upsert({ metadata, value }) {
|
||||
let { id, layerId } = metadata
|
||||
const datalayer = this.getDataLayerFromID(layerId)
|
||||
let feature = this.getFeatureFromMetadata(metadata, value)
|
||||
feature = datalayer.geometryToFeature({ geometry: value.geometry, id, feature })
|
||||
if (feature === undefined) {
|
||||
console.log(`Unable to find feature with id = ${metadata.id}. Creating a new one`)
|
||||
}
|
||||
feature = datalayer.geometryToFeature({
|
||||
geometry: value.geometry,
|
||||
geojson: value,
|
||||
id,
|
||||
feature,
|
||||
})
|
||||
feature.addTo(datalayer)
|
||||
}
|
||||
|
||||
// Update a property of an object
|
||||
update({ key, metadata, value }) {
|
||||
let feature = this.getFeatureFromMetadata(metadata)
|
||||
|
||||
if (feature === undefined) {
|
||||
console.error(`Unable to find feature with id = ${metadata.id}.`)
|
||||
}
|
||||
switch (key) {
|
||||
case 'geometry':
|
||||
const datalayer = this.getDataLayerFromID(metadata.layerId)
|
||||
|
|
|
@ -1249,6 +1249,7 @@ U.Editable = L.Editable.extend({
|
|||
L.Editable.prototype.initialize.call(this, map, options)
|
||||
this.on('editable:drawing:click editable:drawing:move', this.drawingTooltip)
|
||||
this.on('editable:drawing:end', (e) => {
|
||||
console.log('editable:drawing:end')
|
||||
this.map.tooltip.close()
|
||||
// Leaflet.Editable will delete the drawn shape if invalid
|
||||
// (eg. line has only one drawn point)
|
||||
|
@ -1258,14 +1259,21 @@ U.Editable = L.Editable.extend({
|
|||
})
|
||||
// Layer for items added by users
|
||||
this.on('editable:drawing:cancel', (e) => {
|
||||
console.log('editable:drawing:cancel')
|
||||
if (e.layer instanceof U.Marker) e.layer.del()
|
||||
else {
|
||||
// the user might just exit with escape
|
||||
e.layer.onCommit()
|
||||
}
|
||||
})
|
||||
this.on('editable:drawing:commit', function (e) {
|
||||
console.log('editable:drawing:commit')
|
||||
e.layer.isDirty = true
|
||||
if (this.map.editedFeature !== e.layer) e.layer.edit(e)
|
||||
e.layer.onCommit()
|
||||
})
|
||||
this.on('editable:editing', (e) => {
|
||||
console.log('editable:editing')
|
||||
const layer = e.layer
|
||||
layer.isDirty = true
|
||||
if (layer._tooltip && layer.isTooltipOpen()) {
|
||||
|
@ -1274,11 +1282,13 @@ U.Editable = L.Editable.extend({
|
|||
}
|
||||
})
|
||||
this.on('editable:vertex:ctrlclick', (e) => {
|
||||
console.log('editable:vertex:ctrlclick')
|
||||
const index = e.vertex.getIndex()
|
||||
if (index === 0 || (index === e.vertex.getLastIndex() && e.vertex.continue))
|
||||
e.vertex.continue()
|
||||
})
|
||||
this.on('editable:vertex:altclick', (e) => {
|
||||
console.log('editable:vertex:altclick')
|
||||
if (e.vertex.editor.vertexCanBeDeleted(e.vertex)) e.vertex.delete()
|
||||
})
|
||||
this.on('editable:vertex:rawclick', this.onVertexRawClick)
|
||||
|
@ -1366,6 +1376,7 @@ U.Editable = L.Editable.extend({
|
|||
},
|
||||
|
||||
onVertexRawClick: function (e) {
|
||||
console.log('editable:vertex:rawclick')
|
||||
e.layer.onVertexRawClick(e)
|
||||
L.DomEvent.stop(e)
|
||||
e.cancel()
|
||||
|
|
|
@ -15,9 +15,7 @@ U.FeatureMixin = {
|
|||
|
||||
onCommit: function () {
|
||||
const { subject, metadata, engine } = this.getSyncMetadata()
|
||||
engine.upsert(subject, metadata, {
|
||||
geometry: this.getGeometry(),
|
||||
})
|
||||
engine.upsert(subject, metadata, this.toGeoJSON())
|
||||
},
|
||||
|
||||
getGeometry: function () {
|
||||
|
@ -25,6 +23,7 @@ U.FeatureMixin = {
|
|||
},
|
||||
|
||||
syncUpdatedProperties: function (properties) {
|
||||
// When updating latlng, sync the whole geometry
|
||||
if ('latlng'.includes(properties)) {
|
||||
const { subject, metadata, engine } = this.getSyncMetadata()
|
||||
engine.update(subject, metadata, 'geometry', this.getGeometry())
|
||||
|
@ -44,12 +43,16 @@ U.FeatureMixin = {
|
|||
// DataLayer the marker belongs to
|
||||
this.datalayer = options.datalayer || null
|
||||
this.properties = { _umap_options: {} }
|
||||
|
||||
if (options.geojson) {
|
||||
this.populate(options.geojson)
|
||||
}
|
||||
|
||||
if (id) {
|
||||
this.id = id
|
||||
} else {
|
||||
let geojson_id
|
||||
if (options.geojson) {
|
||||
this.populate(options.geojson)
|
||||
geojson_id = options.geojson.id
|
||||
}
|
||||
|
||||
|
@ -189,7 +192,7 @@ U.FeatureMixin = {
|
|||
},
|
||||
|
||||
getAdvancedEditActions: function (container) {
|
||||
const deleteButton = L.DomUtil.createButton(
|
||||
L.DomUtil.createButton(
|
||||
'button umap-delete',
|
||||
container,
|
||||
L._('Delete'),
|
||||
|
@ -939,6 +942,7 @@ U.PathMixin = {
|
|||
|
||||
_onDrag: function () {
|
||||
if (this._tooltip) this._tooltip.setLatLng(this.getCenter())
|
||||
this.syncUpdatedProperties(['latlng'])
|
||||
},
|
||||
|
||||
transferShape: function (at, to) {
|
||||
|
@ -1130,6 +1134,9 @@ U.Polyline = L.Polyline.extend({
|
|||
geojson.geometry.coordinates = [
|
||||
U.Utils.flattenCoordinates(geojson.geometry.coordinates),
|
||||
]
|
||||
|
||||
delete geojson.id // delete the copied id, a new one will be generated.
|
||||
|
||||
const polygon = this.datalayer.geojsonToFeatures(geojson)
|
||||
polygon.edit()
|
||||
this.del()
|
||||
|
@ -1137,7 +1144,7 @@ U.Polyline = L.Polyline.extend({
|
|||
|
||||
getAdvancedEditActions: function (container) {
|
||||
U.FeatureMixin.getAdvancedEditActions.call(this, container)
|
||||
const toPolygon = L.DomUtil.createButton(
|
||||
L.DomUtil.createButton(
|
||||
'button umap-to-polygon',
|
||||
container,
|
||||
L._('Transform to polygon'),
|
||||
|
|
|
@ -257,6 +257,9 @@ U.Map = L.Map.extend({
|
|||
if (this.options.syncEnabled != true) {
|
||||
this.sync.stop()
|
||||
} else {
|
||||
// FIXME: Do this directly in the sync engine, which should check if the engine
|
||||
// is already started or not.
|
||||
|
||||
// Get the authentication token from the server
|
||||
// And pass it to the sync engine.
|
||||
// FIXME: use `this.urls`
|
||||
|
|
|
@ -179,12 +179,18 @@ U.Layer.Choropleth = L.FeatureGroup.extend({
|
|||
return +feature.properties[key] // TODO: should we catch values non castable to int ?
|
||||
},
|
||||
|
||||
computeBreaks: function () {
|
||||
getValues: function () {
|
||||
const values = []
|
||||
this.datalayer.eachLayer((layer) => {
|
||||
let value = this._getValue(layer)
|
||||
if (!isNaN(value)) values.push(value)
|
||||
})
|
||||
return values
|
||||
},
|
||||
|
||||
computeBreaks: function () {
|
||||
const values = this.getValues()
|
||||
|
||||
if (!values.length) {
|
||||
this.options.breaks = []
|
||||
this.options.colors = []
|
||||
|
@ -609,6 +615,7 @@ U.DataLayer = L.Evented.extend({
|
|||
|
||||
render: function (fields, builder) {
|
||||
let impacts = U.Utils.getImpactsFromSchema(fields)
|
||||
console.log('impacts', impacts)
|
||||
|
||||
for (let impact of impacts) {
|
||||
switch (impact) {
|
||||
|
@ -623,6 +630,7 @@ U.DataLayer = L.Evented.extend({
|
|||
fields.forEach((field) => {
|
||||
this.layer.onEdit(field, builder)
|
||||
})
|
||||
this.redraw()
|
||||
this.show()
|
||||
break
|
||||
case 'remote-data':
|
||||
|
@ -1026,7 +1034,7 @@ U.DataLayer = L.Evented.extend({
|
|||
for (i = 0, len = features.length; i < len; i++) {
|
||||
this.geojsonToFeatures(features[i])
|
||||
}
|
||||
return this
|
||||
return this // Why returning "this" ?
|
||||
}
|
||||
|
||||
const geometry = geojson.type === 'Feature' ? geojson.geometry : geojson
|
||||
|
@ -1034,6 +1042,7 @@ U.DataLayer = L.Evented.extend({
|
|||
let feature = this.geometryToFeature({ geometry, geojson })
|
||||
if (feature) {
|
||||
this.addLayer(feature)
|
||||
feature.onCommit()
|
||||
return feature
|
||||
}
|
||||
},
|
||||
|
@ -1597,13 +1606,7 @@ U.DataLayer = L.Evented.extend({
|
|||
// TODO Add an index
|
||||
// For now, iterate on all the features.
|
||||
getFeatureById: function (id) {
|
||||
console.log('looking for feature with id ', id)
|
||||
for (const i in this._layers) {
|
||||
let feature = this._layers[i]
|
||||
if (feature.id === id) {
|
||||
return feature
|
||||
}
|
||||
}
|
||||
return Object.values(this._layers).find((feature) => feature.id === id)
|
||||
},
|
||||
|
||||
getNextFeature: function (feature) {
|
||||
|
|
|
@ -806,7 +806,9 @@ def get_websocket_auth_token(request, map_id, map_inst):
|
|||
)
|
||||
return simple_json_response(token=signed_token)
|
||||
else:
|
||||
return HttpResponseForbidden
|
||||
return HttpResponseForbidden(
|
||||
_("You cannot edit this map with your current permissions.")
|
||||
)
|
||||
|
||||
|
||||
class MapUpdate(FormLessEditMixin, PermissionsMixin, UpdateView):
|
||||
|
|
11
umap/ws.py
11
umap/ws.py
|
@ -33,7 +33,7 @@ class JoinMessage(BaseModel):
|
|||
|
||||
|
||||
class Geometry(BaseModel):
|
||||
type: Literal["Point", "Polygon"]
|
||||
type: Literal["Point", "Polygon", "LineString"]
|
||||
coordinates: list
|
||||
|
||||
|
||||
|
@ -70,11 +70,12 @@ async def join_and_listen(
|
|||
# as doing so beforehand would miss new connections
|
||||
peers = CONNECTIONS[map_id] - {websocket}
|
||||
# Only relay valid "operation" messages
|
||||
try:
|
||||
OperationMessage.model_validate_json(raw_message)
|
||||
except ValidationError as e:
|
||||
print(raw_message, e)
|
||||
# try:
|
||||
# OperationMessage.model_validate_json(raw_message)
|
||||
# except ValidationError as e:
|
||||
print(raw_message)
|
||||
|
||||
# For now, broadcast anyway
|
||||
websockets.broadcast(peers, raw_message)
|
||||
finally:
|
||||
CONNECTIONS[map_id].remove(websocket)
|
||||
|
|
Loading…
Reference in a new issue