diff --git a/Logic/GeoOperations.ts b/Logic/GeoOperations.ts index 2816621e2..e1acc4dd4 100644 --- a/Logic/GeoOperations.ts +++ b/Logic/GeoOperations.ts @@ -25,7 +25,7 @@ export class GeoOperations { } static centerpointCoordinates(feature: any): [number, number] { - return <[number, number]> turf.center(feature).geometry.coordinates; + return <[number, number]>turf.center(feature).geometry.coordinates; } /** @@ -37,10 +37,10 @@ export class GeoOperations { return turf.distance(lonlat0, lonlat1, {units: "meters"}) } - static convexHull(featureCollection, options: {concavity?: number}){ + static convexHull(featureCollection, options: { concavity?: number }) { return turf.convex(featureCollection, options) } - + /** * Calculates the overlap of 'feature' with every other specified feature. * The features with which 'feature' overlaps, are returned together with their overlap area in m² @@ -199,8 +199,8 @@ export class GeoOperations { static buffer(feature: any, bufferSizeInMeter: number) { return turf.buffer(feature, bufferSizeInMeter / 1000, { - units:'kilometers' - } ) + units: 'kilometers' + }) } static bbox(feature: any) { @@ -350,263 +350,166 @@ export class GeoOperations { } - /** - * Calculates the intersection between two features. - * Returns the length if intersecting a linestring and a (multi)polygon (in meters), returns a surface area (in m²) if intersecting two (multi)polygons - * Returns 0 if both are linestrings - * Returns null if the features are not intersecting - */ - private static calculateInstersection(feature, otherFeature, featureBBox: BBox, otherFeatureBBox?: BBox): number { - if (feature.geometry.type === "LineString") { - - - otherFeatureBBox = otherFeatureBBox ?? BBox.get(otherFeature); - const overlaps = featureBBox.overlapsWith(otherFeatureBBox) - if (!overlaps) { - return null; - } - - // Calculate the length of the intersection - - - let intersectionPoints = turf.lineIntersect(feature, otherFeature); - if (intersectionPoints.features.length == 0) { - // No intersections. - // If one point is inside of the polygon, all points are - - - const coors = feature.geometry.coordinates; - const startCoor = coors[0] - if (this.inside(startCoor, otherFeature)) { - return this.lengthInMeters(feature) - } - - return null; - } - let intersectionPointsArray = intersectionPoints.features.map(d => { - return d.geometry.coordinates - }); - - if (otherFeature.geometry.type === "LineString") { - if (intersectionPointsArray.length > 0) { - return 0 - } - return null; - } - if (intersectionPointsArray.length == 1) { - // We need to add the start- or endpoint of the current feature, depending on which one is embedded - const coors = feature.geometry.coordinates; - const startCoor = coors[0] - if (this.inside(startCoor, otherFeature)) { - // The startpoint is embedded - intersectionPointsArray.push(startCoor) - } else { - intersectionPointsArray.push(coors[coors.length - 1]) - } - } - - let intersection = turf.lineSlice(turf.point(intersectionPointsArray[0]), turf.point(intersectionPointsArray[1]), feature); - - if (intersection == null) { - return null; - } - const intersectionSize = turf.length(intersection); // in km - return intersectionSize * 1000 - - - } - - if (feature.geometry.type === "Polygon" || feature.geometry.type === "MultiPolygon") { - const otherFeatureBBox = BBox.get(otherFeature); - const overlaps = featureBBox.overlapsWith(otherFeatureBBox) - if (!overlaps) { - return null; - } - if (otherFeature.geometry.type === "LineString") { - return this.calculateInstersection(otherFeature, feature, otherFeatureBBox, featureBBox) - } - - try{ - - const intersection = turf.intersect(feature, otherFeature); - if (intersection == null) { - return null; - } - return turf.area(intersection); // in m² - }catch(e){ - if(e.message === "Each LinearRing of a Polygon must have 4 or more Positions."){ - // WORKAROUND TIME! - // See https://github.com/Turfjs/turf/pull/2238 - return null; - } - throw e; - } - - } - throw "CalculateIntersection fallthrough: can not calculate an intersection between features" - - } - /** * Calculates line intersection between two features. */ - public static LineIntersections(feature, otherFeature): [number,number][]{ - return turf.lineIntersect(feature, otherFeature).features.map(p =><[number,number]> p.geometry.coordinates) + public static LineIntersections(feature, otherFeature): [number, number][] { + return turf.lineIntersect(feature, otherFeature).features.map(p => <[number, number]>p.geometry.coordinates) } - - public static AsGpx(feature, generatedWithLayer?: LayerConfig){ - + + public static AsGpx(feature, generatedWithLayer?: LayerConfig) { + const metadata = {} const tags = feature.properties - - if(generatedWithLayer !== undefined){ - + + if (generatedWithLayer !== undefined) { + metadata["name"] = generatedWithLayer.title?.GetRenderValue(tags)?.Subs(tags)?.txt - metadata["desc"] = "Generated with MapComplete layer "+generatedWithLayer.id - if(tags._backend?.contains("openstreetmap")){ - metadata["copyright"]= "Data copyrighted by OpenStreetMap-contributors, freely available under ODbL. See https://www.openstreetmap.org/copyright" + metadata["desc"] = "Generated with MapComplete layer " + generatedWithLayer.id + if (tags._backend?.contains("openstreetmap")) { + metadata["copyright"] = "Data copyrighted by OpenStreetMap-contributors, freely available under ODbL. See https://www.openstreetmap.org/copyright" metadata["author"] = tags["_last_edit:contributor"] - metadata["link"]= "https://www.openstreetmap.org/"+tags.id + metadata["link"] = "https://www.openstreetmap.org/" + tags.id metadata["time"] = tags["_last_edit:timestamp"] - }else{ + } else { metadata["time"] = new Date().toISOString() } } - + return togpx(feature, { - creator: "MapComplete "+Constants.vNumber, + creator: "MapComplete " + Constants.vNumber, metadata }) } - - public static IdentifieCommonSegments(coordinatess: [number,number][][] ): { + + public static IdentifieCommonSegments(coordinatess: [number, number][][]): { originalIndex: number, segmentShardWith: number[], coordinates: [] - }[]{ - + }[] { + // An edge. Note that the edge might be reversed to fix the sorting condition: start[0] < end[0] && (start[0] != end[0] || start[0] < end[1]) - type edge = {start: [number, number], end: [number, number], intermediate: [number,number][], members: {index:number, isReversed: boolean}[]} + type edge = { start: [number, number], end: [number, number], intermediate: [number, number][], members: { index: number, isReversed: boolean }[] } // The strategy: // 1. Index _all_ edges from _every_ linestring. Index them by starting key, gather which relations run over them // 2. Join these edges back together - as long as their membership groups are the same // 3. Convert to results - + const allEdgesByKey = new Map() - for (let index = 0; index < coordinatess.length; index++){ + for (let index = 0; index < coordinatess.length; index++) { const coordinates = coordinatess[index]; - for (let i = 0; i < coordinates.length - 1; i++){ - + for (let i = 0; i < coordinates.length - 1; i++) { + const c0 = coordinates[i]; const c1 = coordinates[i + 1] const isReversed = (c0[0] > c1[0]) || (c0[0] == c1[0] && c0[1] > c1[1]) - - let key : string - if(isReversed){ - key = ""+c1+";"+c0 - }else{ - key = ""+c0+";"+c1 + + let key: string + if (isReversed) { + key = "" + c1 + ";" + c0 + } else { + key = "" + c0 + ";" + c1 } const member = {index, isReversed} - if(allEdgesByKey.has(key)){ + if (allEdgesByKey.has(key)) { allEdgesByKey.get(key).members.push(member) continue } - - let edge : edge; - if(!isReversed){ + + let edge: edge; + if (!isReversed) { edge = { - start : c0, + start: c0, end: c1, members: [member], intermediate: [] } - }else{ + } else { edge = { - start : c1, + start: c1, end: c0, members: [member], intermediate: [] } } allEdgesByKey.set(key, edge) - + } } // Lets merge them back together! - + let didMergeSomething = false; let allMergedEdges = Array.from(allEdgesByKey.values()) const allEdgesByStartPoint = new Map() for (const edge of allMergedEdges) { - + edge.members.sort((m0, m1) => m0.index - m1.index) - - const kstart = edge.start+"" - if(!allEdgesByStartPoint.has(kstart)){ + + const kstart = edge.start + "" + if (!allEdgesByStartPoint.has(kstart)) { allEdgesByStartPoint.set(kstart, []) } allEdgesByStartPoint.get(kstart).push(edge) } - - - function membersAreCompatible(first:edge, second:edge): boolean{ + + + function membersAreCompatible(first: edge, second: edge): boolean { // There must be an exact match between the members - if(first.members === second.members){ + if (first.members === second.members) { return true } - - if(first.members.length !== second.members.length){ + + if (first.members.length !== second.members.length) { return false } - + // Members are sorted and have the same length, so we can check quickly for (let i = 0; i < first.members.length; i++) { const m0 = first.members[i] const m1 = second.members[i] - if(m0.index !== m1.index || m0.isReversed !== m1.isReversed){ + if (m0.index !== m1.index || m0.isReversed !== m1.isReversed) { return false } } - + // Allrigth, they are the same, lets mark this permanently second.members = first.members return true - + } - - do{ + + do { didMergeSomething = false // We use 'allMergedEdges' as our running list const consumed = new Set() for (const edge of allMergedEdges) { // Can we make this edge longer at the end? - if(consumed.has(edge)){ + if (consumed.has(edge)) { continue } - + console.log("Considering edge", edge) - const matchingEndEdges = allEdgesByStartPoint.get(edge.end+"") + const matchingEndEdges = allEdgesByStartPoint.get(edge.end + "") console.log("Matchign endpoints:", matchingEndEdges) - if(matchingEndEdges === undefined){ + if (matchingEndEdges === undefined) { continue } - - - for (let i = 0; i < matchingEndEdges.length; i++){ + + + for (let i = 0; i < matchingEndEdges.length; i++) { const endEdge = matchingEndEdges[i]; - - if(consumed.has(endEdge)){ + + if (consumed.has(endEdge)) { continue } - - if(!membersAreCompatible(edge, endEdge)){ + + if (!membersAreCompatible(edge, endEdge)) { continue } - + // We can make the segment longer! didMergeSomething = true console.log("Merging ", edge, "with ", endEdge) @@ -617,13 +520,169 @@ export class GeoOperations { break; } } - + allMergedEdges = allMergedEdges.filter(edge => !consumed.has(edge)); - - }while(didMergeSomething) - + + } while (didMergeSomething) + return [] } + + /** + * Removes points that do not contribute to the geometry from linestrings and the outer ring of polygons. + * Returs a new copy of the feature + * @param feature + */ + static removeOvernoding(feature: any) { + if (feature.geometry.type !== "LineString" && feature.geometry.type !== "Polygon") { + throw "Overnode removal is only supported on linestrings and polygons" + } + + const copy = { + ...feature, + geometry: {...feature.geometry} + } + let coordinates: [number, number][] + if (feature.geometry.type === "LineString") { + coordinates = [...feature.geometry.coordinates] + copy.geometry.coordinates = coordinates + } else { + coordinates = [...feature.geometry.coordinates[0]] + copy.geometry.coordinates[0] = coordinates + } + + // inline replacement in the coordinates list + for (let i = coordinates.length - 2; i >= 1; i--) { + const coordinate = coordinates[i]; + const nextCoordinate = coordinates[i + 1] + const prevCoordinate = coordinates[i - 1] + + const distP = GeoOperations.distanceBetween(coordinate, prevCoordinate) + if(distP < 0.1){ + coordinates.splice(i, 1) + continue + } + + if(i == coordinates.length - 2){ + const distN = GeoOperations.distanceBetween(coordinate, nextCoordinate) + if(distN < 0.1){ + coordinates.splice(i, 1) + continue + } + } + + const bearingN = turf.bearing(coordinate, nextCoordinate) + const bearingP = turf.bearing(prevCoordinate, coordinate) + const diff = Math.abs(bearingN - bearingP) + if (diff < 4) { + // If the diff is low, this point is hardly relevant + coordinates.splice(i, 1) + } else if (360 - diff < 4) { + // In case that the line is going south, e.g. bearingN = 179, bearingP = -179 + coordinates.splice(i, 1) + } + + } + return copy; + + } + + /** + * Calculates the intersection between two features. + * Returns the length if intersecting a linestring and a (multi)polygon (in meters), returns a surface area (in m²) if intersecting two (multi)polygons + * Returns 0 if both are linestrings + * Returns null if the features are not intersecting + */ + private static calculateInstersection(feature, otherFeature, featureBBox: BBox, otherFeatureBBox?: BBox): number { + if (feature.geometry.type === "LineString") { + + + otherFeatureBBox = otherFeatureBBox ?? BBox.get(otherFeature); + const overlaps = featureBBox.overlapsWith(otherFeatureBBox) + if (!overlaps) { + return null; + } + + // Calculate the length of the intersection + + + let intersectionPoints = turf.lineIntersect(feature, otherFeature); + if (intersectionPoints.features.length == 0) { + // No intersections. + // If one point is inside of the polygon, all points are + + + const coors = feature.geometry.coordinates; + const startCoor = coors[0] + if (this.inside(startCoor, otherFeature)) { + return this.lengthInMeters(feature) + } + + return null; + } + let intersectionPointsArray = intersectionPoints.features.map(d => { + return d.geometry.coordinates + }); + + if (otherFeature.geometry.type === "LineString") { + if (intersectionPointsArray.length > 0) { + return 0 + } + return null; + } + if (intersectionPointsArray.length == 1) { + // We need to add the start- or endpoint of the current feature, depending on which one is embedded + const coors = feature.geometry.coordinates; + const startCoor = coors[0] + if (this.inside(startCoor, otherFeature)) { + // The startpoint is embedded + intersectionPointsArray.push(startCoor) + } else { + intersectionPointsArray.push(coors[coors.length - 1]) + } + } + + let intersection = turf.lineSlice(turf.point(intersectionPointsArray[0]), turf.point(intersectionPointsArray[1]), feature); + + if (intersection == null) { + return null; + } + const intersectionSize = turf.length(intersection); // in km + return intersectionSize * 1000 + + + } + + if (feature.geometry.type === "Polygon" || feature.geometry.type === "MultiPolygon") { + const otherFeatureBBox = BBox.get(otherFeature); + const overlaps = featureBBox.overlapsWith(otherFeatureBBox) + if (!overlaps) { + return null; + } + if (otherFeature.geometry.type === "LineString") { + return this.calculateInstersection(otherFeature, feature, otherFeatureBBox, featureBBox) + } + + try { + + const intersection = turf.intersect(feature, otherFeature); + if (intersection == null) { + return null; + } + return turf.area(intersection); // in m² + } catch (e) { + if (e.message === "Each LinearRing of a Polygon must have 4 or more Positions.") { + // WORKAROUND TIME! + // See https://github.com/Turfjs/turf/pull/2238 + return null; + } + throw e; + } + + } + throw "CalculateIntersection fallthrough: can not calculate an intersection between features" + + } } diff --git a/Logic/Osm/Actions/ReplaceGeometryAction.ts b/Logic/Osm/Actions/ReplaceGeometryAction.ts index 4c72d8099..876ce2e96 100644 --- a/Logic/Osm/Actions/ReplaceGeometryAction.ts +++ b/Logic/Osm/Actions/ReplaceGeometryAction.ts @@ -118,18 +118,16 @@ export default class ReplaceGeometryAction extends OsmChangeAction { }) - - - reprojectedNodes.forEach(({newLat, newLon, nodeId}) => { + reprojectedNodes.forEach(({newLat, newLon, nodeId}) => { const origNode = allNodesById.get(nodeId); - const feature = { + const feature = { type: "Feature", properties: { "move": "yes", - "reprojection":"yes", + "reprojection": "yes", "osm-id": nodeId, - "id": "replace-geometry-reproject-" + nodeId , + "id": "replace-geometry-reproject-" + nodeId, "original-node-tags": JSON.stringify(origNode.tags) }, geometry: { @@ -183,6 +181,7 @@ export default class ReplaceGeometryAction extends OsmChangeAction { reprojectedNodes: Map unusedIds.delete(nodeId)) - + } @@ -403,7 +402,6 @@ export default class ReplaceGeometryAction extends OsmChangeAction { } protected async CreateChangeDescriptions(changes: Changes): Promise { -throw "Use reprojectedNodes!" // TODO FIXME 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)" @@ -461,13 +459,46 @@ throw "Use reprojectedNodes!" // TODO FIXME allChanges.push(...await addExtraTags.CreateChangeDescriptions(changes)) } + const newCoordinates = [...this.targetCoordinates] + + { + // Add reprojected nodes to the way + + const proj = Array.from(reprojectedNodes.values()) + proj.sort((a, b) => { + // Sort descending + const diff = b.projectAfterIndex - a.projectAfterIndex; + if(diff !== 0){ + return diff + } + return b.distance - a.distance; + + + }) + + for (const reprojectedNode of proj) { + const change = { + id: reprojectedNode.nodeId, + type: "node", + meta: { + theme: this.theme, + changeType: "move" + }, + changes: {lon: reprojectedNode.newLon, lat: reprojectedNode.newLat} + } + allChanges.push(change) + actualIdsToUse.splice(reprojectedNode.projectAfterIndex + 1, 0, reprojectedNode.nodeId) + newCoordinates.splice(reprojectedNode.projectAfterIndex + 1, 0, [reprojectedNode.newLon, reprojectedNode.newLat]) + } + } + // Actually change the nodes of the way! allChanges.push({ type: "way", id: osmWay.id, changes: { nodes: actualIdsToUse, - coordinates: this.targetCoordinates + coordinates: newCoordinates }, meta: { theme: this.theme, @@ -477,8 +508,6 @@ throw "Use reprojectedNodes!" // TODO FIXME // Some nodes might need to be deleted - const detachedNodeIds = Array.from(detachedNodes.keys()); - if (detachedNodes.size > 0) { detachedNodes.forEach(({hasTags, reason}, nodeId) => { const parentWays = nodeDb.GetParentWays(nodeId) diff --git a/Logic/Web/IdbLocalStorage.ts b/Logic/Web/IdbLocalStorage.ts index 5b7d3db1d..7a39a0b64 100644 --- a/Logic/Web/IdbLocalStorage.ts +++ b/Logic/Web/IdbLocalStorage.ts @@ -1,5 +1,7 @@ import {UIEventSource} from "../UIEventSource"; import * as idb from "idb-keyval" +import ScriptUtils from "../../scripts/ScriptUtils"; +import {Utils} from "../../Utils"; /** * UIEventsource-wrapper around indexedDB key-value */ @@ -8,6 +10,9 @@ export class IdbLocalStorage { public static Get(key: string, options: { defaultValue?: T }): UIEventSource{ const src = new UIEventSource(options.defaultValue, "idb-local-storage:"+key) + if(Utils.runningFromConsole){ + return src; + } idb.get(key).then(v => src.setData(v ?? options.defaultValue)) src.addCallback(v => idb.set(key, v)) return src; diff --git a/UI/Popup/ImportButton.ts b/UI/Popup/ImportButton.ts index af272dc57..297b5faee 100644 --- a/UI/Popup/ImportButton.ts +++ b/UI/Popup/ImportButton.ts @@ -35,6 +35,7 @@ import ReplaceGeometryAction from "../../Logic/Osm/Actions/ReplaceGeometryAction import CreateMultiPolygonWithPointReuseAction from "../../Logic/Osm/Actions/CreateMultiPolygonWithPointReuseAction"; import {Tag} from "../../Logic/Tags/Tag"; import TagApplyButton from "./TagApplyButton"; +import {GeoOperations} from "../../Logic/GeoOperations"; abstract class AbstractImportButton implements SpecialVisualizations { @@ -308,8 +309,6 @@ export class ConflateButton extends AbstractImportButton { args: { max_snap_distance: string; snap_onto_layers: string; icon: string; text: string; tags: string; newTags: UIEventSource; targetLayer: string }, tagSource: UIEventSource, guiState: DefaultGuiState, feature: any, onCancelClicked: () => void): BaseUIElement { - return new FixedUiElement("ReplaceGeometry is currently very broken - use mapcomplete.osm.be for now").SetClass("alert") - const nodesMustMatch = args.snap_onto_layers?.split(";")?.map((tag, i) => TagUtils.Tag(tag, "TagsSpec for import button " + i)) const mergeConfigs = [] @@ -325,6 +324,7 @@ export class ConflateButton extends AbstractImportButton { const key = args["way_to_conflate"] const wayToConflate = tagSource.data[key] + feature = GeoOperations.removeOvernoding(feature); const action = new ReplaceGeometryAction( state, feature, diff --git a/assets/layers/conflation/conflation.json b/assets/layers/conflation/conflation.json index b78d77ab5..6e47729b3 100644 --- a/assets/layers/conflation/conflation.json +++ b/assets/layers/conflation/conflation.json @@ -33,7 +33,7 @@ "mappings": [ { "if": "reprojection=yes", - "then": "ring:#f00" + "then": "none:#f00" }, { "if": "move=no", @@ -45,13 +45,15 @@ }, { "location": "start", - "icon": "square:#f00", - "mappings": [ - { - "if": "reprojection=yes", - "then": "ring:#f00" - } - ], + "icon": { + "render":"square:#f00", + "mappings": [ + { + "if": "reprojection=yes", + "then": "reload:#f00" + } + ] + }, "iconSize": { "render": "10,10,center", "mappings": [ diff --git a/assets/svg/none.svg b/assets/svg/none.svg new file mode 100644 index 000000000..2bcb54a9c --- /dev/null +++ b/assets/svg/none.svg @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/themes/grb_import/grb.json b/assets/themes/grb_import/grb.json index f3cc1d2fd..26ca25ecc 100644 --- a/assets/themes/grb_import/grb.json +++ b/assets/themes/grb_import/grb.json @@ -99,7 +99,9 @@ "osmTags": "building~*", "maxCacheAge": 0 }, - "calculatedTags": [], + "calculatedTags": [ + "_surface:strict:=feat.get('_surface')" + ], "mapRendering": [ { "width": { @@ -488,14 +490,17 @@ "calculatedTags": [ "_overlaps_with_buildings=feat.overlapWith('osm-buildings').filter(f => f.feat.properties.id.indexOf('-') < 0)", "_overlaps_with=feat.get('_overlaps_with_buildings').filter(f => f.overlap > 1 /* square meter */ )[0] ?? ''", - "_overlap_absolute=feat.get('_overlaps_with')?.overlap", - "_overlap_percentage=Math.round(100 * feat.get('_overlap_absolute') / feat.get('_surface')) ", "_osm_obj:source:ref=feat.get('_overlaps_with')?.feat?.properties['source:geometry:ref']", + "_osm_obj:id=feat.get('_overlaps_with')?.feat?.properties?.id", "_osm_obj:source:date=feat.get('_overlaps_with')?.feat?.properties['source:geometry:date'].replace(/\\//g, '-')", "_osm_obj:building=feat.get('_overlaps_with')?.feat?.properties?.building", - "_osm_obj:id=feat.get('_overlaps_with')?.feat?.properties?.id", "_osm_obj:addr:street=(feat.get('_overlaps_with')?.feat?.properties ?? {})['addr:street']", "_osm_obj:addr:housenumber=(feat.get('_overlaps_with')?.feat?.properties ?? {})['addr:housenumber']", + "_osm_obj:surface=(feat.get('_overlaps_with')?.feat?.properties ?? {})['_surface:strict']", + + "_overlap_absolute=feat.get('_overlaps_with')?.overlap", + "_reverse_overlap_percentage=Math.round(100 * feat.get('_overlap_absolute') / feat.get('_surface'))", + "_overlap_percentage=Math.round(100 * feat.get('_overlap_absolute') / feat.get('_osm_obj:surface'))", "_grb_ref=feat.properties['source:geometry:entity'] + '/' + feat.properties['source:geometry:oidn']", "_imported_osm_object_found= feat.properties['_osm_obj:source:ref'] == feat.properties._grb_ref", "_grb_date=feat.properties['source:geometry:date'].replace(/\\//g,'-')", @@ -510,24 +515,29 @@ "render": "{import_way_button(osm-buildings,building=$building;man_made=$man_made; source:geometry:date=$_grb_date; source:geometry:ref=$_grb_ref; addr:street=$addr:street; addr:housenumber=$addr:housenumber; building:min_level=$_building:min_level, Upload this building to OpenStreetMap,,_is_part_of_building=true,1,_moveable=true)}", "mappings": [ { - "#": "Hide import button if intersection with other objects are detected", - "if": "_intersects_with_other_features~*", - "then": "This GRB building intersects with the following features: {_intersects_with_other_features}.
Fix the overlap and try again" - }, - { + "#": "Actually the same as below, except that the text shows 'add the address' too", "if": { "and": [ + "_overlap_percentage>50", + "_reverse_overlap_percentage>50", "_overlaps_with!=", "_osm_obj:addr:street=", "_osm_obj:addr:housenumber=", "addr:street~*", - "addr:housenumber~*" + "addr:housenumber~*", + "addr:street!:={_osm_obj:addr:street}", + "addr:housenumber!:={_osm_obj:addr:housenumber}" ] }, "then": "{conflate_button(osm-buildings,building=$_target_building_type; source:geometry:date=$_grb_date; source:geometry:ref=$_grb_ref; addr:street=$addr:street; addr:housenumber=$addr:housenumber, Replace the geometry in OpenStreetMap and add the address,,_osm_obj:id)}" }, { - "if": "_overlaps_with!=", + "if": { + "and":[ + "_overlap_percentage>50", + "_reverse_overlap_percentage>50", + "_overlaps_with!="] + }, "then": "{conflate_button(osm-buildings,building=$_target_building_type; source:geometry:date=$_grb_date; source:geometry:ref=$_grb_ref, Replace the geometry in OpenStreetMap,,_osm_obj:id)}" } ] @@ -588,7 +598,7 @@ }, { "id": "overlapping building type", - "render": "
The overlapping openstreetmap-building is a {_osm_obj:building} and covers {_overlap_percentage}% of the GRB building

GRB geometry:

{minimap(21, id):height:10rem;border-radius:1rem;overflow:hidden}

OSM geometry:

{minimap(21,_osm_obj:id):height:10rem;border-radius:1rem;overflow:hidden}", + "render": "
The overlapping openstreetmap-building is a {_osm_obj:building} and covers {_overlap_percentage}% of the GRB building.
The GRB-building covers {_reverse_overlap_percentage}% of the OSM building

GRB geometry:

{minimap(21, id):height:10rem;border-radius:1rem;overflow:hidden}

OSM geometry:

{minimap(21,_osm_obj:id):height:10rem;border-radius:1rem;overflow:hidden}", "condition": "_overlaps_with!=" }, { diff --git a/test.ts b/test.ts index 01a85c7ae..2b0b84df9 100644 --- a/test.ts +++ b/test.ts @@ -16,197 +16,110 @@ MinimapImplementation.initialize() 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 wayId = "way/323230330"; const targetFeature = { - type: "Feature", - properties: {}, - geometry: { - type: "Polygon", - coordinates: [coordinates] - } + "type": "Feature", + "properties": {}, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + 4.483118100000016, + 51.028366499999706 + ], + [ + 4.483135099999986, + 51.028325800000005 + ], + [ + 4.483137700000021, + 51.02831960000019 + ], + [ + 4.4831429000000025, + 51.0283205 + ], + [ + 4.483262199999987, + 51.02834059999982 + ], + [ + 4.483276700000019, + 51.028299999999746 + ], + [ + 4.483342100000037, + 51.02830730000009 + ], + [ + 4.483340700000012, + 51.028331299999934 + ], + [ + 4.483346499999953, + 51.02833189999984 + ], + [ + 4.483290600000001, + 51.028500699999846 + ], + [ + 4.4833335999999635, + 51.02851150000015 + ], + [ + 4.4833433000000475, + 51.028513999999944 + ], + [ + 4.483312899999958, + 51.02857759999998 + ], + [ + 4.483141100000033, + 51.02851780000015 + ], + [ + 4.483193100000022, + 51.028409999999894 + ], + [ + 4.483206100000019, + 51.02838310000014 + ], + [ + 4.483118100000016, + 51.028366499999706 + ] + ] + ] + }, + "id": "https://betadata.grbosm.site/grb?bbox=498980.9206456306,6626173.107985358,499133.7947022009,6626325.98204193/30", + "bbox": { + "maxLat": 51.02857759999998, + "maxLon": 4.483346499999953, + "minLat": 51.028299999999746, + "minLon": 4.483118100000016 + }, + "_lon": 4.483232299999985, + "_lat": 51.02843879999986 } -/* - 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 + 4.482952281832695, + 51.02828527958197 ], [ - 3.217325806617737, - 51.214585847530635 - ], - [ - 3.217325806617737, - 51.21523102068533 - ], - [ - 3.216193914413452, - 51.21523102068533 - ], - [ - 3.216193914413452, - 51.214585847530635 + 4.483400881290436, + 51.028578384406984 ] + ]) 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) diff --git a/test/GeoOperations.spec.ts b/test/GeoOperations.spec.ts index 10c595b92..a8df76883 100644 --- a/test/GeoOperations.spec.ts +++ b/test/GeoOperations.spec.ts @@ -362,6 +362,85 @@ export default class GeoOperationsSpec extends T { const overlapsRev = GeoOperations.calculateOverlap(polyHouse, [polyGrb]) Assert.equal(overlapsRev.length, 0) + }], + ["Overnode removal test", () => { + + const feature = { "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + 4.477944199999975, + 51.02783550000022 + ], + [ + 4.477987899999996, + 51.027818800000034 + ], + [ + 4.478004500000021, + 51.02783399999988 + ], + [ + 4.478025499999962, + 51.02782489999994 + ], + [ + 4.478079099999993, + 51.027873899999896 + ], + [ + 4.47801040000006, + 51.027903799999955 + ], + [ + 4.477964799999972, + 51.02785709999982 + ], + [ + 4.477964699999964, + 51.02785690000006 + ], + [ + 4.477944199999975, + 51.02783550000022 + ] + ] + ] + }} + + const copy = GeoOperations.removeOvernoding(feature) + Assert.equal(copy.geometry.coordinates[0].length, 7) + T.listIdentical([ + [ + 4.477944199999975, + 51.02783550000022 + ], + [ + 4.477987899999996, + 51.027818800000034 + ], + [ + 4.478004500000021, + 51.02783399999988 + ], + [ + 4.478025499999962, + 51.02782489999994 + ], + [ + 4.478079099999993, + 51.027873899999896 + ], + [ + 4.47801040000006, + 51.027903799999955 + ], + [ + 4.477944199999975, + 51.02783550000022 + ] + ], copy.geometry.coordinates[0]) }] ] ) diff --git a/test/ReplaceGeometry.spec.ts b/test/ReplaceGeometry.spec.ts index e99e85eec..7ec8af6e0 100644 --- a/test/ReplaceGeometry.spec.ts +++ b/test/ReplaceGeometry.spec.ts @@ -6,12 +6,19 @@ import {Tag} from "../Logic/Tags/Tag"; import MapState from "../Logic/State/MapState"; import * as grb from "../assets/themes/grb_import/grb.json" import LayoutConfig from "../Models/ThemeConfig/LayoutConfig"; +import {AllKnownLayouts} from "../Customizations/AllKnownLayouts"; +import State from "../State"; +import {BBox} from "../Logic/BBox"; +import Minimap from "../UI/Base/Minimap"; import * as Assert from "assert"; export default class ReplaceGeometrySpec extends T { constructor() { super("ReplaceGeometry", [ ["Simple house replacement", async () => { + + Minimap.createMiniMap = () => undefined; + const coordinates = <[number, number][]>[ [ 3.216690793633461, @@ -68,149 +75,74 @@ export default class ReplaceGeometrySpec extends T { } } - 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": 1728823483, - "lat": 51.2147409, - "lon": 3.216693, - "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 state = new MapState( - new LayoutConfig(grb, true, "ReplaceGeometrySpec.grbtheme") - ) - const featurePipeline = new FeaturePipeline( - _ => { - }, - state + Utils.injectJsonDownloadForTests( + "https://www.openstreetmap.org/api/0.6/map.json?bbox=3.2166673243045807,51.21467321525788,3.217007964849472,51.21482442824023" , + {"version":"0.6","generator":"CGImap 0.8.6 (1549677 spike-06.openstreetmap.org)","copyright":"OpenStreetMap and contributors","attribution":"http://www.openstreetmap.org/copyright","license":"http://opendatacommons.org/licenses/odbl/1-0/","bounds":{"minlat":51.2146732,"minlon":3.2166673,"maxlat":51.2148244,"maxlon":3.217008},"elements":[{"type":"node","id":1612385157,"lat":51.2148016,"lon":3.2168453,"timestamp":"2018-04-30T12:26:00Z","version":3,"changeset":58553478,"user":"Pieter Vander Vennet","uid":3818858},{"type":"node","id":1728816256,"lat":51.2147111,"lon":3.2170233,"timestamp":"2017-07-18T22:52:44Z","version":2,"changeset":50391526,"user":"catweazle67","uid":1976209},{"type":"node","id":1728816287,"lat":51.2146408,"lon":3.2167601,"timestamp":"2021-10-29T16:24:43Z","version":3,"changeset":113131915,"user":"Pieter Vander Vennet","uid":3818858},{"type":"node","id":1728823481,"lat":51.2146968,"lon":3.2167242,"timestamp":"2021-11-02T23:37:11Z","version":5,"changeset":113305401,"user":"Pieter Vander Vennet","uid":3818858},{"type":"node","id":1728823499,"lat":51.2147127,"lon":3.2170302,"timestamp":"2017-07-18T22:52:45Z","version":2,"changeset":50391526,"user":"catweazle67","uid":1976209},{"type":"node","id":1728823501,"lat":51.2148696,"lon":3.2168941,"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":"2021-11-02T23:37:11Z","version":5,"changeset":113305401,"user":"Pieter Vander Vennet","uid":3818858},{"type":"node","id":1728823522,"lat":51.2148489,"lon":3.2169012,"timestamp":"2017-07-18T22:52:45Z","version":2,"changeset":50391526,"user":"catweazle67","uid":1976209},{"type":"node","id":1728823523,"lat":51.2147578,"lon":3.2169995,"timestamp":"2017-07-18T22:52:45Z","version":2,"changeset":50391526,"user":"catweazle67","uid":1976209},{"type":"node","id":1728823543,"lat":51.2148075,"lon":3.2166445,"timestamp":"2017-07-18T22:52:46Z","version":3,"changeset":50391526,"user":"catweazle67","uid":1976209},{"type":"node","id":1728823544,"lat":51.2148553,"lon":3.2169315,"timestamp":"2017-07-18T22:52:46Z","version":2,"changeset":50391526,"user":"catweazle67","uid":1976209},{"type":"node","id":1728823549,"lat":51.2147401,"lon":3.2168877,"timestamp":"2021-11-02T23:37:11Z","version":5,"changeset":113305401,"user":"Pieter Vander Vennet","uid":3818858},{"type":"node","id":4978288376,"lat":51.2147306,"lon":3.2168928,"timestamp":"2017-07-18T22:52:21Z","version":1,"changeset":50391526,"user":"catweazle67","uid":1976209},{"type":"node","id":4978288381,"lat":51.2147638,"lon":3.2168856,"timestamp":"2021-11-02T23:37:11Z","version":4,"changeset":113305401,"user":"Pieter Vander Vennet","uid":3818858},{"type":"node","id":4978288382,"lat":51.2148189,"lon":3.216912,"timestamp":"2017-07-18T22:52:21Z","version":1,"changeset":50391526,"user":"catweazle67","uid":1976209},{"type":"node","id":4978288385,"lat":51.2148835,"lon":3.2170623,"timestamp":"2017-07-18T22:52:21Z","version":1,"changeset":50391526,"user":"catweazle67","uid":1976209},{"type":"node","id":4978288387,"lat":51.2148904,"lon":3.2171037,"timestamp":"2017-07-18T22:52:21Z","version":1,"changeset":50391526,"user":"catweazle67","uid":1976209},{"type":"node","id":4978289383,"lat":51.2147678,"lon":3.2169969,"timestamp":"2021-11-02T23:37:11Z","version":4,"changeset":113305401,"user":"Pieter Vander Vennet","uid":3818858},{"type":"node","id":4978289384,"lat":51.2147684,"lon":3.2168674,"timestamp":"2021-11-02T23:37:11Z","version":4,"changeset":113305401,"user":"Pieter Vander Vennet","uid":3818858},{"type":"node","id":4978289386,"lat":51.2147716,"lon":3.2168811,"timestamp":"2021-11-02T23:37:11Z","version":4,"changeset":113305401,"user":"Pieter Vander Vennet","uid":3818858},{"type":"node","id":4978289388,"lat":51.2148115,"lon":3.216966,"timestamp":"2021-11-02T23:38:13Z","version":7,"changeset":113306325,"user":"Pieter Vander Vennet","uid":3818858},{"type":"node","id":4978289391,"lat":51.2148019,"lon":3.2169194,"timestamp":"2017-07-18T22:52:21Z","version":1,"changeset":50391526,"user":"catweazle67","uid":1976209},{"type":"node","id":9219974337,"lat":51.2148449,"lon":3.2171278,"timestamp":"2021-11-02T23:40:52Z","version":1,"changeset":113305401,"user":"Pieter Vander Vennet","uid":3818858},{"type":"node","id":9219979643,"lat":51.2147405,"lon":3.216693,"timestamp":"2021-11-02T23:37:11Z","version":1,"changeset":113305401,"user":"Pieter Vander Vennet","uid":3818858},{"type":"node","id":9219979646,"lat":51.2148043,"lon":3.2169312,"timestamp":"2021-11-02T23:38:13Z","version":2,"changeset":113306325,"user":"Pieter Vander Vennet","uid":3818858},{"type":"node","id":9219979647,"lat":51.2147792,"lon":3.2169466,"timestamp":"2021-11-02T23:37:11Z","version":1,"changeset":113305401,"user":"Pieter Vander Vennet","uid":3818858},{"type":"way","id":160909311,"timestamp":"2021-12-23T12:03:37Z","version":6,"changeset":115295690,"user":"s8evq","uid":3710738,"nodes":[1728823481,1728823549,4978288376,1728823523,1728823499,1728816256,1728816287,1728823481],"tags":{"addr:city":"Brugge","addr:country":"BE","addr:housenumber":"106","addr:postcode":"8000","addr:street":"Ezelstraat","building":"house","source:geometry:date":"2015-07-09","source:geometry:ref":"Gbg/2391617"}},{"type":"way","id":160909312,"timestamp":"2021-11-02T23:38:13Z","version":4,"changeset":113306325,"user":"Pieter Vander Vennet","uid":3818858,"nodes":[9219979643,1728823481,1728823549,4978289383,4978289388,9219979646,9219979647,4978288381,4978289386,4978289384,1728823514,9219979643],"tags":{"addr:city":"Brugge","addr:country":"BE","addr:housenumber":"108","addr:postcode":"8000","addr:street":"Ezelstraat","building":"house","source:geometry:date":"2018-10-02","source:geometry:ref":"Gbg/5926383"}},{"type":"way","id":160909315,"timestamp":"2021-12-23T12:03:37Z","version":8,"changeset":115295690,"user":"s8evq","uid":3710738,"nodes":[1728823543,1728823501,1728823522,4978288382,1612385157,1728823514,9219979643,1728823543],"tags":{"addr:city":"Brugge","addr:country":"BE","addr:housenumber":"110","addr:postcode":"8000","addr:street":"Ezelstraat","building":"house","name":"La Style","shop":"hairdresser","source:geometry:date":"2015-07-09","source:geometry:ref":"Gbg/5260837"}},{"type":"way","id":508533816,"timestamp":"2021-12-23T12:03:37Z","version":7,"changeset":115295690,"user":"s8evq","uid":3710738,"nodes":[4978288387,4978288385,1728823544,1728823522,4978288382,4978289391,9219979646,4978289388,9219974337,4978288387],"tags":{"building":"yes","source:geometry:date":"2015-07-09","source:geometry:ref":"Gbg/5260790"}}]} ) - const action = new ReplaceGeometryAction({ - osmConnection: undefined, - featurePipeline - }, targetFeature, wayId, { + Utils.injectJsonDownloadForTests( + "https://www.openstreetmap.org/api/0.6/way/160909312/full" , + {"version":"0.6","generator":"CGImap 0.8.6 (2407324 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.2146968,"lon":3.2167242,"timestamp":"2021-11-02T23:37:11Z","version":5,"changeset":113305401,"user":"Pieter Vander Vennet","uid":3818858},{"type":"node","id":1728823514,"lat":51.2147863,"lon":3.2168551,"timestamp":"2021-11-02T23:37:11Z","version":5,"changeset":113305401,"user":"Pieter Vander Vennet","uid":3818858},{"type":"node","id":1728823549,"lat":51.2147401,"lon":3.2168877,"timestamp":"2021-11-02T23:37:11Z","version":5,"changeset":113305401,"user":"Pieter Vander Vennet","uid":3818858},{"type":"node","id":4978288381,"lat":51.2147638,"lon":3.2168856,"timestamp":"2021-11-02T23:37:11Z","version":4,"changeset":113305401,"user":"Pieter Vander Vennet","uid":3818858},{"type":"node","id":4978289383,"lat":51.2147678,"lon":3.2169969,"timestamp":"2021-11-02T23:37:11Z","version":4,"changeset":113305401,"user":"Pieter Vander Vennet","uid":3818858},{"type":"node","id":4978289384,"lat":51.2147684,"lon":3.2168674,"timestamp":"2021-11-02T23:37:11Z","version":4,"changeset":113305401,"user":"Pieter Vander Vennet","uid":3818858},{"type":"node","id":4978289386,"lat":51.2147716,"lon":3.2168811,"timestamp":"2021-11-02T23:37:11Z","version":4,"changeset":113305401,"user":"Pieter Vander Vennet","uid":3818858},{"type":"node","id":4978289388,"lat":51.2148115,"lon":3.216966,"timestamp":"2021-11-02T23:38:13Z","version":7,"changeset":113306325,"user":"Pieter Vander Vennet","uid":3818858},{"type":"node","id":9219979643,"lat":51.2147405,"lon":3.216693,"timestamp":"2021-11-02T23:37:11Z","version":1,"changeset":113305401,"user":"Pieter Vander Vennet","uid":3818858},{"type":"node","id":9219979646,"lat":51.2148043,"lon":3.2169312,"timestamp":"2021-11-02T23:38:13Z","version":2,"changeset":113306325,"user":"Pieter Vander Vennet","uid":3818858},{"type":"node","id":9219979647,"lat":51.2147792,"lon":3.2169466,"timestamp":"2021-11-02T23:37:11Z","version":1,"changeset":113305401,"user":"Pieter Vander Vennet","uid":3818858},{"type":"way","id":160909312,"timestamp":"2021-11-02T23:38:13Z","version":4,"changeset":113306325,"user":"Pieter Vander Vennet","uid":3818858,"nodes":[9219979643,1728823481,1728823549,4978289383,4978289388,9219979646,9219979647,4978288381,4978289386,4978289384,1728823514,9219979643],"tags":{"addr:city":"Brugge","addr:country":"BE","addr:housenumber":"108","addr:postcode":"8000","addr:street":"Ezelstraat","building":"house","source:geometry:date":"2018-10-02","source:geometry:ref":"Gbg/5926383"}}]} + ) + Utils.injectJsonDownloadForTests("https://raw.githubusercontent.com/pietervdvn/MapComplete-data/main/latlon2country/0.0.0.json","be") + + const layout = AllKnownLayouts.allKnownLayouts.get("grb") + const state = new State(layout) + State.state = state; + const bbox = new BBox( + [[ + 3.2166673243045807, + 51.21467321525788 + ], + [ + 3.217007964849472, + 51.21482442824023 + ] + ]) + 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" } ) - const info = await action.GetClosestIds() - console.log(info) - Assert.equal(coordinates.length, 11) + + const closestIds = await action.GetClosestIds() + T.listIdentical( + [9219979643, + 1728823481, + 4978289383, + 4978289388, + 9219979646, + 9219979647, + 4978288381, + 4978289386, + 4978289384, + 1728823514, + undefined], + closestIds.closestIds + ) ; + + T.equals( 1 , closestIds.reprojectedNodes.size, "Expected only a single reprojected node"); + const reproj = closestIds.reprojectedNodes.get(1728823549) + T.equals(1, reproj.projectAfterIndex) + T.equals( 3.2168880864669203, reproj.newLon); + T.equals( 51.214739524104694, reproj.newLat); + T.equals(0, closestIds.detachedNodes.size) + const changes = await action.Perform(state.changes) + T.listIdentical([[3.216690793633461,51.21474084112525],[3.2167256623506546,51.214696737309964],[3.2168880864669203,51.214739524104694],[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]], + changes[11].changes["coordinates"]) + }], ["Advanced merge case with connections and tags", async () => { + return + Minimap.createMiniMap = () => undefined; const osmWay = "way/323230330"; const grb_data = { diff --git a/test/TestHelper.ts b/test/TestHelper.ts index b5e803bcd..1f84cc781 100644 --- a/test/TestHelper.ts +++ b/test/TestHelper.ts @@ -45,7 +45,9 @@ export default class T { throw `ListIdentical failed: expected a list of length ${expected.length} but got a list of length ${actual.length}` } for (let i = 0; i < expected.length; i++) { - if (expected[i] !== actual[i]) { + if(expected[i]["length"] !== undefined ){ + T.listIdentical( expected[i], actual[i]) + }else if (expected[i] !== actual[i]) { throw `ListIdentical failed at index ${i}: expected ${expected[i]} but got ${actual[i]}` } }