Add the possibility to snap onto another layer with imports, add location confirm on input, add metalayer exporting all nodes, various fixes
This commit is contained in:
parent
f5d6441b70
commit
23ae9d39c8
24 changed files with 807 additions and 390 deletions
|
@ -26,6 +26,7 @@ import {OsmConnection} from "../Osm/OsmConnection";
|
|||
import {Tiles} from "../../Models/TileRange";
|
||||
import TileFreshnessCalculator from "./TileFreshnessCalculator";
|
||||
import {ElementStorage} from "../ElementStorage";
|
||||
import FullNodeDatabaseSource from "./TiledFeatureSource/FullNodeDatabaseSource";
|
||||
|
||||
|
||||
/**
|
||||
|
@ -146,6 +147,11 @@ export default class FeaturePipeline {
|
|||
|
||||
this.freshnesses.set(id, new TileFreshnessCalculator())
|
||||
|
||||
if(id === "type_node"){
|
||||
// Handles by the 'FullNodeDatabaseSource'
|
||||
continue;
|
||||
}
|
||||
|
||||
if (source.geojsonSource === undefined) {
|
||||
// This is an OSM layer
|
||||
// We load the cached values and register them
|
||||
|
@ -220,6 +226,14 @@ export default class FeaturePipeline {
|
|||
self.freshnesses.get(flayer.layerDef.id).addTileLoad(tileId, new Date())
|
||||
})
|
||||
})
|
||||
|
||||
if(state.layoutToUse.trackAllNodes){
|
||||
new FullNodeDatabaseSource(state, osmFeatureSource, tile => {
|
||||
new RegisteringAllFromFeatureSourceActor(tile)
|
||||
perLayerHierarchy.get(tile.layer.layerDef.id).registerTile(tile)
|
||||
tile.features.addCallbackAndRunD(_ => self.newDataLoadedSignal.setData(tile))
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
const updater = this.initOverpassUpdater(state, useOsmApi)
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
import {UIEventSource} from "../../UIEventSource";
|
||||
import FilteredLayer from "../../../Models/FilteredLayer";
|
||||
import {FeatureSourceForLayer, Tiled} from "../FeatureSource";
|
||||
import {Utils} from "../../../Utils";
|
||||
import {Tiles} from "../../../Models/TileRange";
|
||||
import {BBox} from "../../BBox";
|
||||
|
||||
export default class SimpleFeatureSource implements FeatureSourceForLayer, Tiled {
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
import TileHierarchy from "./TileHierarchy";
|
||||
import FeatureSource, {FeatureSourceForLayer, Tiled} from "../FeatureSource";
|
||||
import {OsmNode, OsmObject, OsmWay} from "../../Osm/OsmObject";
|
||||
import SimpleFeatureSource from "../Sources/SimpleFeatureSource";
|
||||
import {UIEventSource} from "../../UIEventSource";
|
||||
import FilteredLayer from "../../../Models/FilteredLayer";
|
||||
|
||||
|
||||
export default class FullNodeDatabaseSource implements TileHierarchy<FeatureSource & Tiled> {
|
||||
public readonly loadedTiles = new Map<number, FeatureSource & Tiled>()
|
||||
private readonly onTileLoaded: (tile: (Tiled & FeatureSourceForLayer)) => void;
|
||||
private readonly layer : FilteredLayer
|
||||
|
||||
constructor(
|
||||
state: {
|
||||
readonly filteredLayers: UIEventSource<FilteredLayer[]>},
|
||||
osmFeatureSource: { rawDataHandlers: ((data: any, tileId: number) => void)[] },
|
||||
onTileLoaded: ((tile: Tiled & FeatureSourceForLayer) => void)) {
|
||||
this.onTileLoaded = onTileLoaded
|
||||
this.layer = state.filteredLayers.data.filter(l => l.layerDef.id === "type_node")[0]
|
||||
if(this.layer === undefined){
|
||||
throw "Weird: tracking all nodes, but layer 'type_node' is not defined"
|
||||
}
|
||||
const self = this
|
||||
osmFeatureSource.rawDataHandlers.push((osmJson, tileId) => self.handleOsmXml(osmJson, tileId))
|
||||
}
|
||||
|
||||
private handleOsmXml(osmJson: any, tileId: number) {
|
||||
|
||||
const allObjects = OsmObject.ParseObjects(osmJson.elements)
|
||||
const nodesById = new Map<number, OsmNode>()
|
||||
|
||||
for (const osmObj of allObjects) {
|
||||
if (osmObj.type !== "node") {
|
||||
continue
|
||||
}
|
||||
const osmNode = <OsmNode>osmObj;
|
||||
nodesById.set(osmNode.id, osmNode)
|
||||
}
|
||||
|
||||
const parentWaysByNodeId = new Map<number, OsmWay[]>()
|
||||
for (const osmObj of allObjects) {
|
||||
if (osmObj.type !== "way") {
|
||||
continue
|
||||
}
|
||||
const osmWay = <OsmWay>osmObj;
|
||||
for (const nodeId of osmWay.nodes) {
|
||||
|
||||
if (!parentWaysByNodeId.has(nodeId)) {
|
||||
parentWaysByNodeId.set(nodeId, [])
|
||||
}
|
||||
parentWaysByNodeId.get(nodeId).push(osmWay)
|
||||
}
|
||||
}
|
||||
parentWaysByNodeId.forEach((allWays, nodeId) => {
|
||||
nodesById.get(nodeId).tags["parent_ways"] = JSON.stringify(allWays.map(w => w.tags))
|
||||
})
|
||||
const now = new Date()
|
||||
const asGeojsonFeatures = Array.from(nodesById.values()).map(osmNode => ({
|
||||
feature: osmNode.asGeoJson(),freshness: now
|
||||
}))
|
||||
|
||||
const featureSource = new SimpleFeatureSource(this.layer, tileId)
|
||||
featureSource.features.setData(asGeojsonFeatures)
|
||||
this.loadedTiles.set(tileId, featureSource)
|
||||
this.onTileLoaded(featureSource)
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -30,6 +30,8 @@ export default class OsmFeatureSource {
|
|||
};
|
||||
public readonly downloadedTiles = new Set<number>()
|
||||
private readonly allowedTags: TagsFilter;
|
||||
|
||||
public rawDataHandlers: ((osmJson: any, tileId: number) => void)[] = []
|
||||
|
||||
constructor(options: {
|
||||
handleTile: (tile: FeatureSourceForLayer & Tiled) => void;
|
||||
|
@ -94,11 +96,11 @@ export default class OsmFeatureSource {
|
|||
try {
|
||||
|
||||
console.log("Attempting to get tile", z, x, y, "from the osm api")
|
||||
const osmXml = await Utils.download(url, {"accept": "application/xml"})
|
||||
const osmJson = await Utils.downloadJson(url)
|
||||
try {
|
||||
const parsed = new DOMParser().parseFromString(osmXml, "text/xml");
|
||||
console.log("Got tile", z, x, y, "from the osm api")
|
||||
const geojson = OsmToGeoJson.default(parsed,
|
||||
this.rawDataHandlers.forEach(handler => handler(osmJson, Tiles.tile_index(z, x, y)))
|
||||
const geojson = OsmToGeoJson.default(osmJson,
|
||||
// @ts-ignore
|
||||
{
|
||||
flatProperties: true
|
||||
|
|
|
@ -235,6 +235,13 @@ export class GeoOperations {
|
|||
* @param point Point defined as [lon, lat]
|
||||
*/
|
||||
public static nearestPoint(way, point: [number, number]) {
|
||||
if(way.geometry.type === "Polygon"){
|
||||
way = {...way}
|
||||
way.geometry = {...way.geometry}
|
||||
way.geometry.type = "LineString"
|
||||
way.geometry.coordinates = way.geometry.coordinates[0]
|
||||
}
|
||||
|
||||
return turf.nearestPointOnLine(way, point, {units: "kilometers"});
|
||||
}
|
||||
|
||||
|
|
|
@ -4,23 +4,40 @@ import {Changes} from "../Changes";
|
|||
import {Tag} from "../../Tags/Tag";
|
||||
import CreateNewNodeAction from "./CreateNewNodeAction";
|
||||
import {And} from "../../Tags/And";
|
||||
import {TagsFilter} from "../../Tags/TagsFilter";
|
||||
|
||||
export default class CreateNewWayAction extends OsmChangeAction {
|
||||
public newElementId: string = undefined
|
||||
private readonly coordinates: ({ nodeId?: number, lat: number, lon: number })[];
|
||||
private readonly tags: Tag[];
|
||||
private readonly _options: { theme: string };
|
||||
private readonly _options: {
|
||||
theme: string, existingPointHandling?: {
|
||||
withinRangeOfM: number,
|
||||
ifMatches?: TagsFilter,
|
||||
mode: "reuse_osm_point" | "move_osm_point"
|
||||
} []
|
||||
};
|
||||
|
||||
|
||||
/***
|
||||
* Creates a new way to upload to OSM
|
||||
* @param tags: the tags to apply to the wya
|
||||
* @param coordinates: the coordinates. Might have a nodeId, in this case, this node will be used
|
||||
* @param options
|
||||
* @param options
|
||||
*/
|
||||
constructor(tags: Tag[], coordinates: ({ nodeId?: number, lat: number, lon: number })[], options: {
|
||||
theme: string
|
||||
}) {
|
||||
constructor(tags: Tag[], coordinates: ({ nodeId?: number, lat: number, lon: number })[],
|
||||
options: {
|
||||
theme: string,
|
||||
/**
|
||||
* IF specified, an existing OSM-point within this range and satisfying the condition 'ifMatches' will be used instead of a new coordinate.
|
||||
* If multiple points are possible, only the closest point is considered
|
||||
*/
|
||||
existingPointHandling?: {
|
||||
withinRangeOfM: number,
|
||||
ifMatches?: TagsFilter,
|
||||
mode: "reuse_osm_point" | "move_osm_point"
|
||||
} []
|
||||
}) {
|
||||
super()
|
||||
this.coordinates = coordinates;
|
||||
this.tags = tags;
|
||||
|
@ -49,14 +66,14 @@ export default class CreateNewWayAction extends OsmChangeAction {
|
|||
|
||||
// We have all created (or reused) all the points!
|
||||
// Time to create the actual way
|
||||
|
||||
|
||||
|
||||
|
||||
const id = changes.getNewID()
|
||||
|
||||
const newWay = <ChangeDescription> {
|
||||
|
||||
const newWay = <ChangeDescription>{
|
||||
id,
|
||||
type: "way",
|
||||
meta:{
|
||||
meta: {
|
||||
theme: this._options.theme,
|
||||
changeType: "import"
|
||||
},
|
||||
|
@ -67,7 +84,7 @@ export default class CreateNewWayAction extends OsmChangeAction {
|
|||
}
|
||||
}
|
||||
newElements.push(newWay)
|
||||
this.newElementId = "way/"+id
|
||||
this.newElementId = "way/" + id
|
||||
return newElements
|
||||
}
|
||||
|
||||
|
|
|
@ -206,7 +206,7 @@ export abstract class OsmObject {
|
|||
return result;
|
||||
}
|
||||
|
||||
private static ParseObjects(elements: any[]): OsmObject[] {
|
||||
public static ParseObjects(elements: any[]): OsmObject[] {
|
||||
const objects: OsmObject[] = [];
|
||||
const allNodes: Map<number, OsmNode> = new Map<number, OsmNode>()
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue