From 7124cd184c552932aaf8e5599711495607dd050a Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Sat, 1 Jan 2022 01:59:50 +0100 Subject: [PATCH] More work on replaceGeometry --- Logic/Osm/Actions/ReplaceGeometryAction.ts | 82 ++++--- UI/SubstitutedTranslation.ts | 2 +- test.ts | 253 +++++++++++++++++++-- test/ReplaceGeometry.spec.ts | 2 +- 4 files changed, 291 insertions(+), 48 deletions(-) diff --git a/Logic/Osm/Actions/ReplaceGeometryAction.ts b/Logic/Osm/Actions/ReplaceGeometryAction.ts index a6026f96e5..808b62d18e 100644 --- a/Logic/Osm/Actions/ReplaceGeometryAction.ts +++ b/Logic/Osm/Actions/ReplaceGeometryAction.ts @@ -81,8 +81,7 @@ export default class ReplaceGeometryAction extends OsmChangeAction { // noinspection JSUnusedGlobalSymbols public async getPreview(): Promise { - const {closestIds, allNodesById, detachedNodeIds} = await this.GetClosestIds(); - console.debug("Generating preview, identicals are ",) + const {closestIds, allNodesById, detachedNodes} = await this.GetClosestIds(); const preview: GeoJSONObject[] = closestIds.map((newId, i) => { if (this.identicalTo[i] !== undefined) { return undefined @@ -116,13 +115,14 @@ export default class ReplaceGeometryAction extends OsmChangeAction { }; }) - for (const detachedNodeId of detachedNodeIds) { - const origPoint = allNodesById.get(detachedNodeId).centerpoint() + detachedNodes.forEach(({reason}, id) => { + const origPoint = allNodesById.get(id).centerpoint() const feature = { type: "Feature", properties: { "detach": "yes", - "id": "replace-geometry-detach-" + detachedNodeId + "id": "replace-geometry-detach-" + id, + "detach-reason":reason }, geometry: { type: "Point", @@ -130,7 +130,7 @@ export default class ReplaceGeometryAction extends OsmChangeAction { } }; preview.push(feature) - } + }) return new StaticFeatureSource(Utils.NoNull(preview), false) @@ -142,8 +142,13 @@ export default class ReplaceGeometryAction extends OsmChangeAction { const allChanges: ChangeDescription[] = [] const actualIdsToUse: number[] = [] - const {closestIds, osmWay, detachedNodeIds} = await this.GetClosestIds() + const nodeDb = this.state.featurePipeline.fullNodeDatabase; + if (nodeDb === undefined) { + throw "PANIC: replaceGeometryAction needs the FullNodeDatabase, which is undefined. This should be initialized by having the 'type_node'-layer enabled in your theme. (NB: the replacebutton has type_node as dependency)" + } + const {closestIds, osmWay, detachedNodes} = await this.GetClosestIds() + const detachedNodeIds = Array.from(detachedNodes.keys()); for (let i = 0; i < closestIds.length; i++) { if (this.identicalTo[i] !== undefined) { const j = this.identicalTo[i] @@ -211,10 +216,7 @@ export default class ReplaceGeometryAction extends OsmChangeAction { // Some nodes might need to be deleted if (detachedNodeIds.length > 0) { - const nodeDb = this.state.featurePipeline.fullNodeDatabase; - if (nodeDb === undefined) { - throw "PANIC: replaceGeometryAction needs the FullNodeDatabase, which is undefined. This should be initialized by having the 'type_node'-layer enabled in your theme. (NB: the replacebutton has type_node as dependency)" - } + for (const nodeId of detachedNodeIds) { const parentWays = nodeDb.GetParentWays(nodeId) const index = parentWays.data.map(w => w.id).indexOf(osmWay.id) @@ -260,12 +262,18 @@ export default class ReplaceGeometryAction extends OsmChangeAction { closestIds: number[], allNodesById: Map, osmWay: OsmWay, - detachedNodeIds: number[] + detachedNodes: Map }> { // TODO FIXME: cap move length on points which are embedded into other ways (ev. disconnect them) // TODO FIXME: if a new point has to be created, snap to already existing ways - + const nodeDb = this.state.featurePipeline.fullNodeDatabase; + if (nodeDb === undefined) { + throw "PANIC: replaceGeometryAction needs the FullNodeDatabase, which is undefined. This should be initialized by having the 'type_node'-layer enabled in your theme. (NB: the replacebutton has type_node as dependency)" + } + let parsed: OsmObject[]; { // Gather the needed OsmObjects @@ -295,18 +303,27 @@ export default class ReplaceGeometryAction extends OsmChangeAction { const distances = new Map distance (or undefined if a duplicate)*/ number[]>(); + + const nodeInfo = new Map() + for (const node of allNodes) { - const nodesWithAllParents = this.state.featurePipeline.fullNodeDatabase - if (nodesWithAllParents === undefined) { - throw "PANIC: replaceGeometryAction needs the FullNodeDatabase, which is undefined. This should be initialized by having the 'type_node'-layer enabled in your theme. (NB: the replacebutton has type_node as dependency)" + + const parentWays = nodeDb.GetParentWays(node.id) + if(parentWays === undefined){ + throw "PANIC: the way to replace has a node which has no parents at all. Is it deleted in the meantime?" } - - let parentWayIds = nodesWithAllParents.GetParentWays(node.id) - - - - - + const parentWayIds = parentWays.data.map(w => w.type+"/"+w.id) + const idIndex = parentWayIds.indexOf(this.wayToReplaceId) + if(idIndex < 0){ + throw "PANIC: the way to replace has a node, which is _not_ part of this was according to the node..." + } + parentWayIds.splice(idIndex, 1) + const partOfSomeWay = parentWayIds.length > 0 const nodeDistances = this.targetCoordinates.map(_ => undefined) @@ -319,10 +336,17 @@ export default class ReplaceGeometryAction extends OsmChangeAction { nodeDistances[i] = GeoOperations.distanceBetween(targetCoordinate, [cp[1], cp[0]]) } distances.set(node.id, nodeDistances) + nodeInfo.set(node.id, { + distances: nodeDistances, + partOfWay: partOfSomeWay, + hasTags: Object.keys(node.tags).length > 1 + }) } const closestIds = this.targetCoordinates.map(_ => undefined) - const unusedIds = [] + const unusedIds = new Map(); { /** * Then, we search the node that has to move the least distance and add this as mapping. @@ -372,7 +396,9 @@ export default class ReplaceGeometryAction extends OsmChangeAction { }) } else { // Seems like all the targetCoordinates have found a source point - unusedIds.push(candidate) + unusedIds.set(candidate,{ + reason: "Unused by new way" + }) } } } while (candidate !== undefined) @@ -380,7 +406,9 @@ export default class ReplaceGeometryAction extends OsmChangeAction { // If there are still unused values in 'distances', they are definitively unused distances.forEach((_, nodeId) => { - unusedIds.push(nodeId) + unusedIds.set(nodeId,{ + reason: "Unused by new way" + }) }) { @@ -393,7 +421,7 @@ export default class ReplaceGeometryAction extends OsmChangeAction { for (const node of allNodes) { allNodesById.set(node.id, node) } - return {closestIds, allNodesById, osmWay, detachedNodeIds: unusedIds}; + return {closestIds, allNodesById, osmWay, detachedNodes: unusedIds}; } } diff --git a/UI/SubstitutedTranslation.ts b/UI/SubstitutedTranslation.ts index 69f57683f5..c753875dc6 100644 --- a/UI/SubstitutedTranslation.ts +++ b/UI/SubstitutedTranslation.ts @@ -53,7 +53,7 @@ export class SubstitutedTranslation extends VariableUiElement { return viz.func.constr(State.state, tagsSource, proto.special.args, DefaultGuiState.state).SetStyle(proto.special.style); } catch (e) { console.error("SPECIALRENDERING FAILED for", tagsSource.data?.id, e) - return new FixedUiElement(`Could not generate special rendering for ${viz.func}(${viz.args.join(", ")}) ${e}`).SetStyle("alert") + return new FixedUiElement(`Could not generate special rendering for ${viz.func.funcName}(${viz.args.join(", ")}) ${e}`).SetStyle("alert") } } )) diff --git a/test.ts b/test.ts index 7e9e298ad0..01a85c7ae6 100644 --- a/test.ts +++ b/test.ts @@ -1,24 +1,239 @@ -import BackgroundMapSwitch from "./UI/BigComponents/BackgroundMapSwitch"; -import {UIEventSource} from "./Logic/UIEventSource"; -import Loc from "./Models/Loc"; +import State from "./State"; +import AllKnownLayers from "./Customizations/AllKnownLayers"; import AvailableBaseLayersImplementation from "./Logic/Actors/AvailableBaseLayersImplementation"; import AvailableBaseLayers from "./Logic/Actors/AvailableBaseLayers"; -import BaseLayer from "./Models/BaseLayer"; -import {VariableUiElement} from "./UI/Base/VariableUIElement"; +import MinimapImplementation from "./UI/Base/MinimapImplementation"; +import {Utils} from "./Utils"; +import * as grb from "./assets/themes/grb_import/grb.json"; +import ReplaceGeometryAction from "./Logic/Osm/Actions/ReplaceGeometryAction"; +import Minimap from "./UI/Base/Minimap"; +import ShowDataLayer from "./UI/ShowDataLayer/ShowDataLayer"; +import {AllKnownLayouts} from "./Customizations/AllKnownLayouts"; +import {BBox} from "./Logic/BBox"; AvailableBaseLayers.implement(new AvailableBaseLayersImplementation()) -const state = { - currentBackground: new UIEventSource(AvailableBaseLayers.osmCarto), - locationControl: new UIEventSource({ - zoom: 19, - lat: 51.2, - lon: 3.2 - }) -} -const actualBackground = new UIEventSource(AvailableBaseLayers.osmCarto) -new BackgroundMapSwitch(state, - { - currentBackground: actualBackground - }).AttachTo("maindiv") +MinimapImplementation.initialize() -new VariableUiElement(actualBackground.map(bg => bg.id)).AttachTo("extradiv") \ No newline at end of file +async function test() { + + const coordinates = <[number, number][]>[ + [ + 3.216690793633461, + 51.21474084112525 + ], + [ + 3.2167256623506546, + 51.214696737309964 + ], + [ + 3.2169999182224274, + 51.214768983537674 + ], + [ + 3.2169650495052338, + 51.21480720678671 + ], + [ + 3.2169368863105774, + 51.21480090625335 + ], + [ + 3.2169489562511444, + 51.21478074454077 + ], + [ + 3.216886594891548, + 51.214765203214625 + ], + [ + 3.2168812304735184, + 51.21477192378873 + ], + [ + 3.2168644666671753, + 51.214768983537674 + ], + [ + 3.2168537378311157, + 51.21478746511261 + ], + [ + 3.216690793633461, + 51.21474084112525 + ] + ] + + const targetFeature = { + type: "Feature", + properties: {}, + geometry: { + type: "Polygon", + coordinates: [coordinates] + } + } +/* + Utils.injectJsonDownloadForTests( + "https://www.openstreetmap.org/api/0.6/way/160909312/full", + { + "version": "0.6", + "generator": "CGImap 0.8.5 (920083 spike-06.openstreetmap.org)", + "copyright": "OpenStreetMap and contributors", + "attribution": "http://www.openstreetmap.org/copyright", + "license": "http://opendatacommons.org/licenses/odbl/1-0/", + "elements": [{ + "type": "node", + "id": 1728823481, + "lat": 51.2146969, + "lon": 3.2167247, + "timestamp": "2017-07-18T22:52:45Z", + "version": 2, + "changeset": 50391526, + "user": "catweazle67", + "uid": 1976209 + }, { + "type": "node", + "id": 1728823514, + "lat": 51.2147863, + "lon": 3.2168551, + "timestamp": "2017-07-18T22:52:45Z", + "version": 2, + "changeset": 50391526, + "user": "catweazle67", + "uid": 1976209 + }, { + "type": "node", + "id": 1728823549, + "lat": 51.2147399, + "lon": 3.2168871, + "timestamp": "2017-07-18T22:52:46Z", + "version": 2, + "changeset": 50391526, + "user": "catweazle67", + "uid": 1976209 + }, { + "type": "node", + "id": 4978288381, + "lat": 51.2147638, + "lon": 3.2168856, + "timestamp": "2017-07-18T22:52:21Z", + "version": 1, + "changeset": 50391526, + "user": "catweazle67", + "uid": 1976209 + }, { + "type": "node", + "id": 4978289383, + "lat": 51.2147676, + "lon": 3.2169973, + "timestamp": "2017-07-18T22:52:21Z", + "version": 1, + "changeset": 50391526, + "user": "catweazle67", + "uid": 1976209 + }, { + "type": "node", + "id": 4978289384, + "lat": 51.2147683, + "lon": 3.2168674, + "timestamp": "2017-07-18T22:52:21Z", + "version": 1, + "changeset": 50391526, + "user": "catweazle67", + "uid": 1976209 + }, { + "type": "node", + "id": 4978289386, + "lat": 51.2147718, + "lon": 3.2168815, + "timestamp": "2017-07-18T22:52:21Z", + "version": 1, + "changeset": 50391526, + "user": "catweazle67", + "uid": 1976209 + }, { + "type": "node", + "id": 4978289388, + "lat": 51.2147884, + "lon": 3.2169829, + "timestamp": "2017-07-18T22:52:21Z", + "version": 1, + "changeset": 50391526, + "user": "catweazle67", + "uid": 1976209 + }, { + "type": "way", + "id": 160909312, + "timestamp": "2017-07-18T22:52:30Z", + "version": 2, + "changeset": 50391526, + "user": "catweazle67", + "uid": 1976209, + "nodes": [1728823483, 1728823514, 4978289384, 4978289386, 4978288381, 4978289388, 4978289383, 1728823549, 1728823481, 1728823483], + "tags": { + "addr:city": "Brugge", + "addr:country": "BE", + "addr:housenumber": "108", + "addr:postcode": "8000", + "addr:street": "Ezelstraat", + "building": "yes" + } + }] + } + ) +*/ + const wayId = "way/160909312" + + const layout = AllKnownLayouts.allKnownLayouts.get("grb") + const state = new State(layout) + State.state = state; + const bbox = new BBox( + [[ + 3.216193914413452, + 51.214585847530635 + ], + [ + 3.217325806617737, + 51.214585847530635 + ], + [ + 3.217325806617737, + 51.21523102068533 + ], + [ + 3.216193914413452, + 51.21523102068533 + ], + [ + 3.216193914413452, + 51.214585847530635 + ] + ]) + const url = `https://www.openstreetmap.org/api/0.6/map.json?bbox=${bbox.minLon},${bbox.minLat},${bbox.maxLon},${bbox.maxLat}` + const data = await Utils.downloadJson(url) + + state.featurePipeline.fullNodeDatabase.handleOsmJson(data, 0) + + + const action = new ReplaceGeometryAction(state, targetFeature, wayId, { + theme: "test" + } + ) + + console.log(">>>>> ", action.GetClosestIds()) + + const map = Minimap.createMiniMap({ + attribution: false, + }) + const preview = await action.getPreview() + new ShowDataLayer({ + layerToShow: AllKnownLayers.sharedLayers.get("conflation"), + features: preview, + leafletMap: map.leafletMap, + zoomToFeatures: true + }) + map + .SetStyle("height: 75vh;") + .AttachTo("maindiv") +} + +test() \ No newline at end of file diff --git a/test/ReplaceGeometry.spec.ts b/test/ReplaceGeometry.spec.ts index eed8d79764..e99e85eece 100644 --- a/test/ReplaceGeometry.spec.ts +++ b/test/ReplaceGeometry.spec.ts @@ -64,7 +64,7 @@ export default class ReplaceGeometrySpec extends T { properties: {}, geometry: { type: "Polygon", - coordinates + coordinates: [coordinates] } }