Add metadata in changeset with (binned) distance to changed feature
This commit is contained in:
parent
e8ce53d5eb
commit
8e66313ef1
21 changed files with 178 additions and 41 deletions
|
@ -6,6 +6,18 @@ import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig";
|
||||||
import {QueryParameters} from "../Web/QueryParameters";
|
import {QueryParameters} from "../Web/QueryParameters";
|
||||||
import FeatureSource from "../FeatureSource/FeatureSource";
|
import FeatureSource from "../FeatureSource/FeatureSource";
|
||||||
|
|
||||||
|
export interface GeoLocationPointProperties {
|
||||||
|
id: "gps",
|
||||||
|
"user:location": "yes",
|
||||||
|
"date": string,
|
||||||
|
"latitude": number
|
||||||
|
"longitude":number,
|
||||||
|
"speed": number,
|
||||||
|
"accuracy": number
|
||||||
|
"heading": number
|
||||||
|
"altitude":number
|
||||||
|
}
|
||||||
|
|
||||||
export default class GeoLocationHandler extends VariableUiElement {
|
export default class GeoLocationHandler extends VariableUiElement {
|
||||||
|
|
||||||
private readonly currentLocation: FeatureSource
|
private readonly currentLocation: FeatureSource
|
||||||
|
@ -184,10 +196,9 @@ export default class GeoLocationHandler extends VariableUiElement {
|
||||||
this.currentLocation = state.currentUserLocation
|
this.currentLocation = state.currentUserLocation
|
||||||
this._currentGPSLocation.addCallback((location) => {
|
this._currentGPSLocation.addCallback((location) => {
|
||||||
self._previousLocationGrant.setData("granted");
|
self._previousLocationGrant.setData("granted");
|
||||||
console.log("Location is", location,)
|
|
||||||
const feature = {
|
const feature = {
|
||||||
"type": "Feature",
|
"type": "Feature",
|
||||||
properties: {
|
properties: <GeoLocationPointProperties>{
|
||||||
id: "gps",
|
id: "gps",
|
||||||
"user:location": "yes",
|
"user:location": "yes",
|
||||||
"date": new Date().toISOString(),
|
"date": new Date().toISOString(),
|
||||||
|
|
|
@ -20,7 +20,11 @@ export interface ChangeDescription {
|
||||||
/**
|
/**
|
||||||
* THe motivation for the change, e.g. 'deleted because does not exist anymore'
|
* THe motivation for the change, e.g. 'deleted because does not exist anymore'
|
||||||
*/
|
*/
|
||||||
specialMotivation?: string
|
specialMotivation?: string,
|
||||||
|
/**
|
||||||
|
* Added by Changes.ts
|
||||||
|
*/
|
||||||
|
distanceToObject?: number
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -11,7 +11,7 @@ export default class ChangeLocationAction extends OsmChangeAction {
|
||||||
theme: string,
|
theme: string,
|
||||||
reason: string
|
reason: string
|
||||||
}) {
|
}) {
|
||||||
super();
|
super(id,true);
|
||||||
if (!id.startsWith("node/")) {
|
if (!id.startsWith("node/")) {
|
||||||
throw "Invalid ID: only 'node/number' is accepted"
|
throw "Invalid ID: only 'node/number' is accepted"
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ export default class ChangeLocationAction extends OsmChangeAction {
|
||||||
this._newLonLat = newLonLat;
|
this._newLonLat = newLonLat;
|
||||||
this._meta = meta;
|
this._meta = meta;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async CreateChangeDescriptions(changes: Changes): Promise<ChangeDescription[]> {
|
protected async CreateChangeDescriptions(changes: Changes): Promise<ChangeDescription[]> {
|
||||||
|
|
||||||
const d: ChangeDescription = {
|
const d: ChangeDescription = {
|
||||||
|
|
|
@ -13,13 +13,13 @@ export default class ChangeTagAction extends OsmChangeAction {
|
||||||
theme: string,
|
theme: string,
|
||||||
changeType: "answer" | "soft-delete" | "add-image" | string
|
changeType: "answer" | "soft-delete" | "add-image" | string
|
||||||
}) {
|
}) {
|
||||||
super();
|
super(elementId, true);
|
||||||
this._elementId = elementId;
|
this._elementId = elementId;
|
||||||
this._tagsFilter = tagsFilter;
|
this._tagsFilter = tagsFilter;
|
||||||
this._currentTags = currentTags;
|
this._currentTags = currentTags;
|
||||||
this._meta = meta;
|
this._meta = meta;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Doublechecks that no stupid values are added
|
* Doublechecks that no stupid values are added
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -31,7 +31,7 @@ export default class CreateNewNodeAction extends OsmChangeAction {
|
||||||
reusePointWithinMeters?: number,
|
reusePointWithinMeters?: number,
|
||||||
theme: string, changeType: "create" | "import" | null
|
theme: string, changeType: "create" | "import" | null
|
||||||
}) {
|
}) {
|
||||||
super()
|
super(null,basicTags !== undefined && basicTags.length > 0)
|
||||||
this._basicTags = basicTags;
|
this._basicTags = basicTags;
|
||||||
this._lat = lat;
|
this._lat = lat;
|
||||||
this._lon = lon;
|
this._lon = lon;
|
||||||
|
|
|
@ -24,13 +24,13 @@ export default class CreateNewWayAction extends OsmChangeAction {
|
||||||
options: {
|
options: {
|
||||||
theme: string
|
theme: string
|
||||||
}) {
|
}) {
|
||||||
super()
|
super(null,true)
|
||||||
this.coordinates = coordinates;
|
this.coordinates = coordinates;
|
||||||
this.tags = tags;
|
this.tags = tags;
|
||||||
this._options = options;
|
this._options = options;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async CreateChangeDescriptions(changes: Changes): Promise<ChangeDescription[]> {
|
public async CreateChangeDescriptions(changes: Changes): Promise<ChangeDescription[]> {
|
||||||
|
|
||||||
const newElements: ChangeDescription[] = []
|
const newElements: ChangeDescription[] = []
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ export default class CreateNewWayAction extends OsmChangeAction {
|
||||||
changeType: null,
|
changeType: null,
|
||||||
theme: this._options.theme
|
theme: this._options.theme
|
||||||
})
|
})
|
||||||
await changes.applyAction(newPoint)
|
newElements.push(...await newPoint.CreateChangeDescriptions(changes))
|
||||||
pointIds.push(newPoint.newElementIdNumber)
|
pointIds.push(newPoint.newElementIdNumber)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,7 +46,7 @@ export default class CreateWayWithPointReuseAction extends OsmChangeAction {
|
||||||
state: FeaturePipelineState,
|
state: FeaturePipelineState,
|
||||||
config: MergePointConfig[]
|
config: MergePointConfig[]
|
||||||
) {
|
) {
|
||||||
super();
|
super(null,true);
|
||||||
this._tags = tags;
|
this._tags = tags;
|
||||||
this._state = state;
|
this._state = state;
|
||||||
this._config = config;
|
this._config = config;
|
||||||
|
@ -194,9 +194,8 @@ export default class CreateWayWithPointReuseAction extends OsmChangeAction {
|
||||||
const newWay = new CreateNewWayAction(this._tags, nodeIdsToUse, {
|
const newWay = new CreateNewWayAction(this._tags, nodeIdsToUse, {
|
||||||
theme
|
theme
|
||||||
})
|
})
|
||||||
|
|
||||||
allChanges.push(...(await newWay.Perform(changes)))
|
allChanges.push(...(await newWay.CreateChangeDescriptions(changes)))
|
||||||
|
|
||||||
return allChanges
|
return allChanges
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@ export default class DeleteAction extends OsmChangeAction {
|
||||||
specialMotivation: string
|
specialMotivation: string
|
||||||
},
|
},
|
||||||
hardDelete: boolean) {
|
hardDelete: boolean) {
|
||||||
super()
|
super(id,true)
|
||||||
this._id = id;
|
this._id = id;
|
||||||
this._hardDelete = hardDelete;
|
this._hardDelete = hardDelete;
|
||||||
this.meta = {...meta, changeType: "deletion"};
|
this.meta = {...meta, changeType: "deletion"};
|
||||||
|
|
|
@ -8,6 +8,18 @@ import {ChangeDescription} from "./ChangeDescription";
|
||||||
export default abstract class OsmChangeAction {
|
export default abstract class OsmChangeAction {
|
||||||
|
|
||||||
private isUsed = false
|
private isUsed = false
|
||||||
|
public readonly trackStatistics: boolean;
|
||||||
|
/**
|
||||||
|
* The ID of the object that is the center of this change.
|
||||||
|
* Null if the action creates a new object
|
||||||
|
* Undefined if such an id does not make sense
|
||||||
|
*/
|
||||||
|
public readonly mainObjectId: string;
|
||||||
|
|
||||||
|
constructor(mainObjectId: string, trackStatistics: boolean = true) {
|
||||||
|
this.trackStatistics = trackStatistics;
|
||||||
|
this.mainObjectId = mainObjectId
|
||||||
|
}
|
||||||
|
|
||||||
public Perform(changes: Changes) {
|
public Perform(changes: Changes) {
|
||||||
if (this.isUsed) {
|
if (this.isUsed) {
|
||||||
|
@ -18,6 +30,4 @@ export default abstract class OsmChangeAction {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract CreateChangeDescriptions(changes: Changes): Promise<ChangeDescription[]>
|
protected abstract CreateChangeDescriptions(changes: Changes): Promise<ChangeDescription[]>
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -16,11 +16,10 @@ abstract class AbstractRelationSplitHandler extends OsmChangeAction {
|
||||||
protected readonly _theme: string;
|
protected readonly _theme: string;
|
||||||
|
|
||||||
constructor(input: RelationSplitInput, theme: string) {
|
constructor(input: RelationSplitInput, theme: string) {
|
||||||
super()
|
super("relation/"+input.relation.id, false)
|
||||||
this._input = input;
|
this._input = input;
|
||||||
this._theme = theme;
|
this._theme = theme;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns which node should border the member at the given index
|
* Returns which node should border the member at the given index
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -41,7 +41,7 @@ export default class ReplaceGeometryAction extends OsmChangeAction {
|
||||||
newTags?: Tag[]
|
newTags?: Tag[]
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
super();
|
super(wayToReplaceId, false);
|
||||||
this.state = state;
|
this.state = state;
|
||||||
this.feature = feature;
|
this.feature = feature;
|
||||||
this.wayToReplaceId = wayToReplaceId;
|
this.wayToReplaceId = wayToReplaceId;
|
||||||
|
|
|
@ -26,7 +26,7 @@ export default class SplitAction extends OsmChangeAction {
|
||||||
* @param toleranceInMeters: if a splitpoint closer then this amount of meters to an existing point, the existing point will be used to split the line instead of a new point
|
* @param toleranceInMeters: if a splitpoint closer then this amount of meters to an existing point, the existing point will be used to split the line instead of a new point
|
||||||
*/
|
*/
|
||||||
constructor(wayId: string, splitPointCoordinates: [number, number][], meta: { theme: string }, toleranceInMeters = 5) {
|
constructor(wayId: string, splitPointCoordinates: [number, number][], meta: { theme: string }, toleranceInMeters = 5) {
|
||||||
super()
|
super(wayId,true)
|
||||||
this.wayId = wayId;
|
this.wayId = wayId;
|
||||||
this._splitPointsCoordinates = splitPointCoordinates
|
this._splitPointsCoordinates = splitPointCoordinates
|
||||||
this._toleranceInMeters = toleranceInMeters;
|
this._toleranceInMeters = toleranceInMeters;
|
||||||
|
|
|
@ -8,6 +8,11 @@ import {Utils} from "../../Utils";
|
||||||
import {LocalStorageSource} from "../Web/LocalStorageSource";
|
import {LocalStorageSource} from "../Web/LocalStorageSource";
|
||||||
import SimpleMetaTagger from "../SimpleMetaTagger";
|
import SimpleMetaTagger from "../SimpleMetaTagger";
|
||||||
import CreateNewNodeAction from "./Actions/CreateNewNodeAction";
|
import CreateNewNodeAction from "./Actions/CreateNewNodeAction";
|
||||||
|
import FeatureSource from "../FeatureSource/FeatureSource";
|
||||||
|
import {ElementStorage} from "../ElementStorage";
|
||||||
|
import {GeoLocationPointProperties} from "../Actors/GeoLocationHandler";
|
||||||
|
import {GeoOperations} from "../GeoOperations";
|
||||||
|
import {ChangesetTag} from "./ChangesetHandler";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles all changes made to OSM.
|
* Handles all changes made to OSM.
|
||||||
|
@ -27,6 +32,8 @@ export class Changes {
|
||||||
|
|
||||||
private readonly previouslyCreated: OsmObject[] = []
|
private readonly previouslyCreated: OsmObject[] = []
|
||||||
private readonly _leftRightSensitive: boolean;
|
private readonly _leftRightSensitive: boolean;
|
||||||
|
|
||||||
|
private _state : { allElements: ElementStorage; historicalUserLocations: FeatureSource }
|
||||||
|
|
||||||
constructor(leftRightSensitive: boolean = false) {
|
constructor(leftRightSensitive: boolean = false) {
|
||||||
this._leftRightSensitive = leftRightSensitive;
|
this._leftRightSensitive = leftRightSensitive;
|
||||||
|
@ -113,14 +120,71 @@ export class Changes {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
public async applyAction(action: OsmChangeAction): Promise<void> {
|
private calculateDistanceToChanges(change: OsmChangeAction, changeDescriptions: ChangeDescription[]){
|
||||||
this.applyChanges(await action.Perform(this))
|
|
||||||
}
|
|
||||||
|
|
||||||
public async applyActions(actions: OsmChangeAction[]) {
|
if (this._state === undefined) {
|
||||||
for (const action of actions) {
|
// No state loaded -> we can't calculate...
|
||||||
await this.applyAction(action)
|
return;
|
||||||
}
|
}
|
||||||
|
if(!change.trackStatistics){
|
||||||
|
// Probably irrelevant, such as a new helper node
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const now = new Date()
|
||||||
|
const recentLocationPoints = this._state.historicalUserLocations.features.data.map(ff => ff.feature)
|
||||||
|
.filter(feat => feat.geometry.type === "Point")
|
||||||
|
.filter(feat => {
|
||||||
|
const visitTime = new Date((<GeoLocationPointProperties>feat.properties).date)
|
||||||
|
// In seconds
|
||||||
|
const diff = (now.getTime() - visitTime.getTime()) / 1000
|
||||||
|
return diff < Constants.nearbyVisitTime;
|
||||||
|
})
|
||||||
|
if(recentLocationPoints.length === 0){
|
||||||
|
// Probably no GPS enabled/no fix
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The applicable points, contain information in their properties about location, time and GPS accuracy
|
||||||
|
// They are all GeoLocationPointProperties
|
||||||
|
// We walk every change and determine the closest distance possible
|
||||||
|
// Only if the change itself does _not_ contain any coordinates, we fall back and search the original feature in the state
|
||||||
|
|
||||||
|
const changedObjectCoordinates : [number, number][] = []
|
||||||
|
|
||||||
|
const feature = this._state.allElements.ContainingFeatures.get(change.mainObjectId)
|
||||||
|
if(feature !== undefined){
|
||||||
|
changedObjectCoordinates.push(GeoOperations.centerpointCoordinates(feature))
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const changeDescription of changeDescriptions) {
|
||||||
|
const chng : {lat: number, lon: number} | {coordinates : [number,number][]} | {members} = changeDescription.changes
|
||||||
|
if(chng === undefined){
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if(chng["lat"] !== undefined){
|
||||||
|
changedObjectCoordinates.push([chng["lat"],chng["lon"]])
|
||||||
|
}
|
||||||
|
if(chng["coordinates"] !== undefined){
|
||||||
|
changedObjectCoordinates.push(...chng["coordinates"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const leastDistance = Math.min(...changedObjectCoordinates.map(coor =>
|
||||||
|
Math.min(...recentLocationPoints.map(gpsPoint => {
|
||||||
|
const otherCoor = GeoOperations.centerpointCoordinates(gpsPoint)
|
||||||
|
const dist = GeoOperations.distanceBetween(coor, otherCoor) * 1000;
|
||||||
|
console.log("Comparing ", coor, "and ", otherCoor, " --> ", dist)
|
||||||
|
return dist
|
||||||
|
}))
|
||||||
|
))
|
||||||
|
return leastDistance
|
||||||
|
}
|
||||||
|
|
||||||
|
public async applyAction(action: OsmChangeAction): Promise<void> {
|
||||||
|
const changeDescriptions = await action.Perform(this)
|
||||||
|
const distanceToObject = this.calculateDistanceToChanges(action, changeDescriptions)
|
||||||
|
changeDescriptions[0].meta.distanceToObject = distanceToObject
|
||||||
|
this.applyChanges(changeDescriptions)
|
||||||
}
|
}
|
||||||
|
|
||||||
public applyChanges(changes: ChangeDescription[]) {
|
public applyChanges(changes: ChangeDescription[]) {
|
||||||
|
@ -130,6 +194,13 @@ export class Changes {
|
||||||
this.allChanges.data.push(...changes)
|
this.allChanges.data.push(...changes)
|
||||||
this.allChanges.ping()
|
this.allChanges.ping()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public useLocationHistory(state: {
|
||||||
|
allElements: ElementStorage,
|
||||||
|
historicalUserLocations: FeatureSource
|
||||||
|
}){
|
||||||
|
this._state= state
|
||||||
|
}
|
||||||
|
|
||||||
public registerIdRewrites(mappings: Map<string, string>): void {
|
public registerIdRewrites(mappings: Map<string, string>): void {
|
||||||
CreateNewNodeAction.registerIdRewrites(mappings)
|
CreateNewNodeAction.registerIdRewrites(mappings)
|
||||||
|
@ -162,7 +233,6 @@ export class Changes {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
const meta = pending[0].meta
|
|
||||||
|
|
||||||
const perType = Array.from(
|
const perType = Array.from(
|
||||||
Utils.Hist(pending.filter(descr => descr.meta.changeType !== undefined && descr.meta.changeType !== null)
|
Utils.Hist(pending.filter(descr => descr.meta.changeType !== undefined && descr.meta.changeType !== null)
|
||||||
|
@ -177,16 +247,46 @@ export class Changes {
|
||||||
key: descr.meta.changeType + ":" + descr.type + "/" + descr.id,
|
key: descr.meta.changeType + ":" + descr.type + "/" + descr.id,
|
||||||
value: descr.meta.specialMotivation
|
value: descr.meta.specialMotivation
|
||||||
}))
|
}))
|
||||||
const metatags = [{
|
|
||||||
|
const distances = Utils.NoNull(pending.map(descr => descr.meta.distanceToObject));
|
||||||
|
distances.sort((a, b) => a - b)
|
||||||
|
const perBinCount = Constants.distanceToChangeObjectBins.map(_ => 0)
|
||||||
|
|
||||||
|
let j = 0;
|
||||||
|
const maxDistances = Constants.distanceToChangeObjectBins
|
||||||
|
for (let i = 0; i < maxDistances.length; i++){
|
||||||
|
const maxDistance = maxDistances[i];
|
||||||
|
// distances is sorted in ascending order, so as soon as one is to big, all the resting elements will be bigger too
|
||||||
|
while(j < distances.length && distances[j] < maxDistance){
|
||||||
|
perBinCount[i] ++
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const perBinMessage = Utils.NoNull(perBinCount.map((count, i) => {
|
||||||
|
if(count === 0){
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
key: "change_within_"+maxDistances[i]+"m",
|
||||||
|
value: count,
|
||||||
|
aggregate:true
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
|
||||||
|
// This method is only called with changedescriptions for this theme
|
||||||
|
const theme = pending[0].meta.theme
|
||||||
|
const metatags : ChangesetTag[] = [{
|
||||||
key: "comment",
|
key: "comment",
|
||||||
value: "Adding data with #MapComplete for theme #" + meta.theme
|
value: "Adding data with #MapComplete for theme #" + theme
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "theme",
|
key: "theme",
|
||||||
value: meta.theme
|
value: theme
|
||||||
},
|
},
|
||||||
...perType,
|
...perType,
|
||||||
...motivations
|
...motivations,
|
||||||
|
...perBinMessage
|
||||||
]
|
]
|
||||||
|
|
||||||
await State.state.osmConnection.changesetHandler.UploadChangeset(
|
await State.state.osmConnection.changesetHandler.UploadChangeset(
|
||||||
|
|
|
@ -78,6 +78,7 @@ export class ChangesetHandler {
|
||||||
}
|
}
|
||||||
if (this._dryRun) {
|
if (this._dryRun) {
|
||||||
const changesetXML = generateChangeXML(123456);
|
const changesetXML = generateChangeXML(123456);
|
||||||
|
console.log("Metatags are", extraMetaTags)
|
||||||
console.log(changesetXML);
|
console.log(changesetXML);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import {Utils} from "../../Utils";
|
||||||
import ChangeToElementsActor from "../Actors/ChangeToElementsActor";
|
import ChangeToElementsActor from "../Actors/ChangeToElementsActor";
|
||||||
import PendingChangesUploader from "../Actors/PendingChangesUploader";
|
import PendingChangesUploader from "../Actors/PendingChangesUploader";
|
||||||
import TitleHandler from "../Actors/TitleHandler";
|
import TitleHandler from "../Actors/TitleHandler";
|
||||||
|
import FeatureSource from "../FeatureSource/FeatureSource";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The part of the state keeping track of where the elements, loading them, configuring the feature pipeline etc
|
* The part of the state keeping track of where the elements, loading them, configuring the feature pipeline etc
|
||||||
|
@ -50,7 +51,6 @@ export default class ElementsState extends FeatureSwitchState {
|
||||||
super(layoutToUse);
|
super(layoutToUse);
|
||||||
|
|
||||||
this.changes = new Changes(layoutToUse?.isLeftRightSensitive() ?? false)
|
this.changes = new Changes(layoutToUse?.isLeftRightSensitive() ?? false)
|
||||||
|
|
||||||
{
|
{
|
||||||
// -- Location control initialization
|
// -- Location control initialization
|
||||||
const zoom = UIEventSource.asFloat(
|
const zoom = UIEventSource.asFloat(
|
||||||
|
|
|
@ -14,7 +14,7 @@ import {QueryParameters} from "../Web/QueryParameters";
|
||||||
import * as personal from "../../assets/themes/personal/personal.json";
|
import * as personal from "../../assets/themes/personal/personal.json";
|
||||||
import FilterConfig from "../../Models/ThemeConfig/FilterConfig";
|
import FilterConfig from "../../Models/ThemeConfig/FilterConfig";
|
||||||
import ShowOverlayLayer from "../../UI/ShowDataLayer/ShowOverlayLayer";
|
import ShowOverlayLayer from "../../UI/ShowDataLayer/ShowOverlayLayer";
|
||||||
import FeatureSource, {FeatureSourceForLayer, Tiled} from "../FeatureSource/FeatureSource";
|
import {FeatureSourceForLayer, Tiled} from "../FeatureSource/FeatureSource";
|
||||||
import SimpleFeatureSource from "../FeatureSource/Sources/SimpleFeatureSource";
|
import SimpleFeatureSource from "../FeatureSource/Sources/SimpleFeatureSource";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -209,7 +209,6 @@ export default class MapState extends UserRelatedState {
|
||||||
const feature = JSON.parse(JSON.stringify(location.feature))
|
const feature = JSON.parse(JSON.stringify(location.feature))
|
||||||
feature.properties.id = "gps/"+i
|
feature.properties.id = "gps/"+i
|
||||||
i++
|
i++
|
||||||
console.log("New location: ", feature)
|
|
||||||
features.data.push({feature, freshness: new Date()})
|
features.data.push({feature, freshness: new Date()})
|
||||||
histCoordinates.push(feature.geometry.coordinates)
|
histCoordinates.push(feature.geometry.coordinates)
|
||||||
|
|
||||||
|
@ -224,7 +223,7 @@ export default class MapState extends UserRelatedState {
|
||||||
|
|
||||||
let gpsLayerDef: FilteredLayer = this.filteredLayers.data.filter(l => l.layerDef.id === "gps_track")[0]
|
let gpsLayerDef: FilteredLayer = this.filteredLayers.data.filter(l => l.layerDef.id === "gps_track")[0]
|
||||||
this.historicalUserLocations = new SimpleFeatureSource(gpsLayerDef, Tiles.tile_index(0, 0, 0), features);
|
this.historicalUserLocations = new SimpleFeatureSource(gpsLayerDef, Tiles.tile_index(0, 0, 0), features);
|
||||||
|
this.changes.useLocationHistory(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
private initHomeLocation() {
|
private initHomeLocation() {
|
||||||
|
|
|
@ -11,6 +11,7 @@ import ElementsState from "./ElementsState";
|
||||||
import SelectedElementTagsUpdater from "../Actors/SelectedElementTagsUpdater";
|
import SelectedElementTagsUpdater from "../Actors/SelectedElementTagsUpdater";
|
||||||
import StaticFeatureSource from "../FeatureSource/Sources/StaticFeatureSource";
|
import StaticFeatureSource from "../FeatureSource/Sources/StaticFeatureSource";
|
||||||
import FeatureSource from "../FeatureSource/FeatureSource";
|
import FeatureSource from "../FeatureSource/FeatureSource";
|
||||||
|
import {Feature} from "@turf/turf";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The part of the state which keeps track of user-related stuff, e.g. the OSM-connection,
|
* The part of the state which keeps track of user-related stuff, e.g. the OSM-connection,
|
||||||
|
|
|
@ -2,7 +2,7 @@ import {Utils} from "../Utils";
|
||||||
|
|
||||||
export default class Constants {
|
export default class Constants {
|
||||||
|
|
||||||
public static vNumber = "0.12.3";
|
public static vNumber = "0.12.4";
|
||||||
public static ImgurApiKey = '7070e7167f0a25a'
|
public static ImgurApiKey = '7070e7167f0a25a'
|
||||||
public static readonly mapillary_client_token_v3 = 'TXhLaWthQ1d4RUg0czVxaTVoRjFJZzowNDczNjUzNmIyNTQyYzI2'
|
public static readonly mapillary_client_token_v3 = 'TXhLaWthQ1d4RUg0czVxaTVoRjFJZzowNDczNjUzNmIyNTQyYzI2'
|
||||||
public static readonly mapillary_client_token_v4 = "MLY|4441509239301885|b40ad2d3ea105435bd40c7e76993ae85"
|
public static readonly mapillary_client_token_v4 = "MLY|4441509239301885|b40ad2d3ea105435bd40c7e76993ae85"
|
||||||
|
@ -39,6 +39,19 @@ export default class Constants {
|
||||||
* (Note that pendingChanges might upload sooner if the popup is closed or similar)
|
* (Note that pendingChanges might upload sooner if the popup is closed or similar)
|
||||||
*/
|
*/
|
||||||
static updateTimeoutSec: number = 30;
|
static updateTimeoutSec: number = 30;
|
||||||
|
/**
|
||||||
|
* If the contributor has their GPS location enabled and makes a change,
|
||||||
|
* the points visited less then `nearbyVisitTime`-seconds ago will be inspected.
|
||||||
|
* The point closest to the changed feature will be considered and this distance will be tracked.
|
||||||
|
* ALl these distances are used to calculate a nearby-score
|
||||||
|
*/
|
||||||
|
static nearbyVisitTime: number= 30 * 60;
|
||||||
|
/**
|
||||||
|
* If a user makes a change, the distance to the changed object is calculated.
|
||||||
|
* If a user makes multiple changes, all these distances are put into multiple bins, depending on this distance.
|
||||||
|
* For every bin, the totals are uploaded as metadata
|
||||||
|
*/
|
||||||
|
static distanceToChangeObjectBins = [25,50,100,500,1000,5000]
|
||||||
|
|
||||||
private static isRetina(): boolean {
|
private static isRetina(): boolean {
|
||||||
if (Utils.runningFromConsole) {
|
if (Utils.runningFromConsole) {
|
||||||
|
|
|
@ -15,7 +15,7 @@ export default class AllThemesGui {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
new FixedUiElement("").AttachTo("centermessage")
|
new FixedUiElement("").AttachTo("centermessage")
|
||||||
const state = new UserRelatedState(undefined);
|
const state = new UserRelatedState(undefined, undefined);
|
||||||
const intro = new Combine([
|
const intro = new Combine([
|
||||||
LanguagePicker.CreateLanguagePicker(Translations.t.index.title.SupportedLanguages())
|
LanguagePicker.CreateLanguagePicker(Translations.t.index.title.SupportedLanguages())
|
||||||
.SetClass("absolute top-2 right-3"),
|
.SetClass("absolute top-2 right-3"),
|
||||||
|
|
|
@ -95,7 +95,7 @@ export default class SplitRoadWizard extends Toggle {
|
||||||
const points = splitPoints.data.map((f, i) => [f.feature, i])
|
const points = splitPoints.data.map((f, i) => [f.feature, i])
|
||||||
.filter(p => GeoOperations.distanceBetween(p[0].geometry.coordinates, coordinates) * 1000 < 5)
|
.filter(p => GeoOperations.distanceBetween(p[0].geometry.coordinates, coordinates) * 1000 < 5)
|
||||||
.map(p => p[1])
|
.map(p => p[1])
|
||||||
.sort()
|
.sort((a, b) => a - b)
|
||||||
.reverse()
|
.reverse()
|
||||||
if (points.length > 0) {
|
if (points.length > 0) {
|
||||||
for (const point of points) {
|
for (const point of points) {
|
||||||
|
|
|
@ -52,7 +52,7 @@ export default class ActorsSpec extends T {
|
||||||
[
|
[
|
||||||
"download latest version",
|
"download latest version",
|
||||||
() => {
|
() => {
|
||||||
const state = new UserRelatedState(AllKnownLayouts.allKnownLayouts.get("bookcases"))
|
const state = new UserRelatedState(AllKnownLayouts.allKnownLayouts.get("bookcases"), undefined)
|
||||||
const feature = {
|
const feature = {
|
||||||
"type": "Feature",
|
"type": "Feature",
|
||||||
"id": "node/5568693115",
|
"id": "node/5568693115",
|
||||||
|
|
Loading…
Reference in a new issue