forked from MapComplete/MapComplete
UX: move map to note location if a hash is given in the note, fix #2311
This commit is contained in:
parent
cc28932534
commit
3fb8f7342f
3 changed files with 47 additions and 34 deletions
|
@ -4,10 +4,12 @@ import { LocalStorageSource } from "../Web/LocalStorageSource"
|
||||||
import { QueryParameters } from "../Web/QueryParameters"
|
import { QueryParameters } from "../Web/QueryParameters"
|
||||||
import Hash from "../Web/Hash"
|
import Hash from "../Web/Hash"
|
||||||
import OsmObjectDownloader from "../Osm/OsmObjectDownloader"
|
import OsmObjectDownloader from "../Osm/OsmObjectDownloader"
|
||||||
import { OsmObject } from "../Osm/OsmObject"
|
|
||||||
import Constants from "../../Models/Constants"
|
import Constants from "../../Models/Constants"
|
||||||
import { Utils } from "../../Utils"
|
import { Utils } from "../../Utils"
|
||||||
import { GeoLocationState } from "../State/GeoLocationState"
|
import { GeoLocationState } from "../State/GeoLocationState"
|
||||||
|
import { OsmConnection } from "../Osm/OsmConnection"
|
||||||
|
|
||||||
|
"use strict"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This actor is responsible to set the map location.
|
* This actor is responsible to set the map location.
|
||||||
|
@ -25,7 +27,7 @@ export default class InitialMapPositioning {
|
||||||
public location: UIEventSource<{ lon: number; lat: number }>
|
public location: UIEventSource<{ lon: number; lat: number }>
|
||||||
public useTerrain: Store<boolean>
|
public useTerrain: Store<boolean>
|
||||||
|
|
||||||
constructor(layoutToUse: ThemeConfig, geolocationState: GeoLocationState) {
|
constructor(layoutToUse: ThemeConfig, geolocationState: GeoLocationState, osmConnection: OsmConnection) {
|
||||||
function localStorageSynced(
|
function localStorageSynced(
|
||||||
key: string,
|
key: string,
|
||||||
deflt: number,
|
deflt: number,
|
||||||
|
@ -47,7 +49,6 @@ export default class InitialMapPositioning {
|
||||||
return src
|
return src
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialHash = Hash.hash.data
|
|
||||||
|
|
||||||
// -- Location control initialization
|
// -- Location control initialization
|
||||||
this.zoom = localStorageSynced(
|
this.zoom = localStorageSynced(
|
||||||
|
@ -72,6 +73,7 @@ export default class InitialMapPositioning {
|
||||||
})
|
})
|
||||||
this.useTerrain = new ImmutableStore<boolean>(layoutToUse.enableTerrain)
|
this.useTerrain = new ImmutableStore<boolean>(layoutToUse.enableTerrain)
|
||||||
|
|
||||||
|
const initialHash = Hash.hash.data
|
||||||
if (initialHash?.match(/^(node|way|relation)\/[0-9]+$/)) {
|
if (initialHash?.match(/^(node|way|relation)\/[0-9]+$/)) {
|
||||||
// We pan to the selected element
|
// We pan to the selected element
|
||||||
const [type, id] = initialHash.split("/")
|
const [type, id] = initialHash.split("/")
|
||||||
|
@ -88,6 +90,15 @@ export default class InitialMapPositioning {
|
||||||
const [lat, lon] = osmObject.centerpoint()
|
const [lat, lon] = osmObject.centerpoint()
|
||||||
this.location.setData({ lon, lat })
|
this.location.setData({ lon, lat })
|
||||||
})
|
})
|
||||||
|
} else if (layoutToUse.id === "notes" && initialHash?.match(/[0-9]+/)) {
|
||||||
|
console.log("Loading note", initialHash)
|
||||||
|
const noteId = Number(initialHash)
|
||||||
|
osmConnection.getNote(noteId).then(note => {
|
||||||
|
const [lon, lat] = note.geometry.coordinates
|
||||||
|
console.log("Got note:", note)
|
||||||
|
this.location.set({ lon, lat })
|
||||||
|
}
|
||||||
|
)
|
||||||
} else if (
|
} else if (
|
||||||
Constants.GeoIpServer &&
|
Constants.GeoIpServer &&
|
||||||
lat.data === defaultLat &&
|
lat.data === defaultLat &&
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { Utils } from "../../Utils"
|
||||||
import { LocalStorageSource } from "../Web/LocalStorageSource"
|
import { LocalStorageSource } from "../Web/LocalStorageSource"
|
||||||
import { AuthConfig } from "./AuthConfig"
|
import { AuthConfig } from "./AuthConfig"
|
||||||
import Constants from "../../Models/Constants"
|
import Constants from "../../Models/Constants"
|
||||||
|
import { Feature, Point } from "geojson"
|
||||||
|
|
||||||
interface OsmUserInfo {
|
interface OsmUserInfo {
|
||||||
id: number
|
id: number
|
||||||
|
@ -218,7 +219,7 @@ export class OsmConnection {
|
||||||
this.auth.xhr(
|
this.auth.xhr(
|
||||||
{
|
{
|
||||||
method: "GET",
|
method: "GET",
|
||||||
path: "/api/0.6/user/details",
|
path: "/api/0.6/user/details"
|
||||||
},
|
},
|
||||||
(err, details: XMLDocument) => {
|
(err, details: XMLDocument) => {
|
||||||
if (err != null) {
|
if (err != null) {
|
||||||
|
@ -330,9 +331,9 @@ export class OsmConnection {
|
||||||
method,
|
method,
|
||||||
headers: header,
|
headers: header,
|
||||||
content,
|
content,
|
||||||
path: `/api/0.6/${path}`,
|
path: `/api/0.6/${path}`
|
||||||
},
|
},
|
||||||
function (err, response) {
|
function(err, response) {
|
||||||
if (err !== null) {
|
if (err !== null) {
|
||||||
error(err)
|
error(err)
|
||||||
} else {
|
} else {
|
||||||
|
@ -412,7 +413,7 @@ export class OsmConnection {
|
||||||
"notes.json",
|
"notes.json",
|
||||||
content,
|
content,
|
||||||
{
|
{
|
||||||
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
|
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"
|
||||||
},
|
},
|
||||||
true
|
true
|
||||||
)
|
)
|
||||||
|
@ -423,6 +424,12 @@ export class OsmConnection {
|
||||||
return id
|
return id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async getNote(id: number): Promise<Feature<Point>> {
|
||||||
|
return JSON.parse(await this.get(
|
||||||
|
"notes/" + id + ".json"
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
public static GpxTrackVisibility = ["private", "public", "trackable", "identifiable"] as const
|
public static GpxTrackVisibility = ["private", "public", "trackable", "identifiable"] as const
|
||||||
|
|
||||||
public async uploadGpxTrack(
|
public async uploadGpxTrack(
|
||||||
|
@ -453,7 +460,7 @@ export class OsmConnection {
|
||||||
file: gpx,
|
file: gpx,
|
||||||
description: options.description,
|
description: options.description,
|
||||||
tags: options.labels?.join(",") ?? "",
|
tags: options.labels?.join(",") ?? "",
|
||||||
visibility: options.visibility,
|
visibility: options.visibility
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!contents.description) {
|
if (!contents.description) {
|
||||||
|
@ -461,9 +468,9 @@ export class OsmConnection {
|
||||||
}
|
}
|
||||||
const extras = {
|
const extras = {
|
||||||
file:
|
file:
|
||||||
'; filename="' +
|
"; filename=\"" +
|
||||||
(options.filename ?? "gpx_track_mapcomplete_" + new Date().toISOString()) +
|
(options.filename ?? "gpx_track_mapcomplete_" + new Date().toISOString()) +
|
||||||
'"\r\nContent-Type: application/gpx+xml',
|
"\"\r\nContent-Type: application/gpx+xml"
|
||||||
}
|
}
|
||||||
|
|
||||||
const boundary = "987654"
|
const boundary = "987654"
|
||||||
|
@ -471,7 +478,7 @@ export class OsmConnection {
|
||||||
let body = ""
|
let body = ""
|
||||||
for (const key in contents) {
|
for (const key in contents) {
|
||||||
body += "--" + boundary + "\r\n"
|
body += "--" + boundary + "\r\n"
|
||||||
body += 'Content-Disposition: form-data; name="' + key + '"'
|
body += "Content-Disposition: form-data; name=\"" + key + "\""
|
||||||
if (extras[key] !== undefined) {
|
if (extras[key] !== undefined) {
|
||||||
body += extras[key]
|
body += extras[key]
|
||||||
}
|
}
|
||||||
|
@ -482,7 +489,7 @@ export class OsmConnection {
|
||||||
|
|
||||||
const response = await this.post("gpx/create", body, {
|
const response = await this.post("gpx/create", body, {
|
||||||
"Content-Type": "multipart/form-data; boundary=" + boundary,
|
"Content-Type": "multipart/form-data; boundary=" + boundary,
|
||||||
"Content-Length": "" + body.length,
|
"Content-Length": "" + body.length
|
||||||
})
|
})
|
||||||
const parsed = JSON.parse(response)
|
const parsed = JSON.parse(response)
|
||||||
console.log("Uploaded GPX track", parsed)
|
console.log("Uploaded GPX track", parsed)
|
||||||
|
@ -503,9 +510,9 @@ export class OsmConnection {
|
||||||
{
|
{
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
|
||||||
path: `/api/0.6/notes/${id}/comment?text=${encodeURIComponent(text)}`,
|
path: `/api/0.6/notes/${id}/comment?text=${encodeURIComponent(text)}`
|
||||||
},
|
},
|
||||||
function (err) {
|
function(err) {
|
||||||
if (err !== null) {
|
if (err !== null) {
|
||||||
error(err)
|
error(err)
|
||||||
} else {
|
} else {
|
||||||
|
@ -520,7 +527,7 @@ export class OsmConnection {
|
||||||
* To be called by land.html
|
* To be called by land.html
|
||||||
*/
|
*/
|
||||||
public finishLogin(callback: (previousURL: string) => void) {
|
public finishLogin(callback: (previousURL: string) => void) {
|
||||||
this.auth.authenticate(function () {
|
this.auth.authenticate(function() {
|
||||||
// Fully authed at this point
|
// Fully authed at this point
|
||||||
console.log("Authentication successful!")
|
console.log("Authentication successful!")
|
||||||
const previousLocation = LocalStorageSource.get("location_before_login")
|
const previousLocation = LocalStorageSource.get("location_before_login")
|
||||||
|
@ -541,7 +548,7 @@ export class OsmConnection {
|
||||||
*/
|
*/
|
||||||
singlepage: !this._iframeMode,
|
singlepage: !this._iframeMode,
|
||||||
auto: true,
|
auto: true,
|
||||||
apiUrl: this._oauth_config.api_url ?? this._oauth_config.url,
|
apiUrl: this._oauth_config.api_url ?? this._oauth_config.url
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,11 +2,7 @@ import ThemeConfig from "./ThemeConfig/ThemeConfig"
|
||||||
import { SpecialVisualizationState } from "../UI/SpecialVisualization"
|
import { SpecialVisualizationState } from "../UI/SpecialVisualization"
|
||||||
import { Changes } from "../Logic/Osm/Changes"
|
import { Changes } from "../Logic/Osm/Changes"
|
||||||
import { Store, UIEventSource } from "../Logic/UIEventSource"
|
import { Store, UIEventSource } from "../Logic/UIEventSource"
|
||||||
import {
|
import { FeatureSource, IndexedFeatureSource, WritableFeatureSource } from "../Logic/FeatureSource/FeatureSource"
|
||||||
FeatureSource,
|
|
||||||
IndexedFeatureSource,
|
|
||||||
WritableFeatureSource,
|
|
||||||
} from "../Logic/FeatureSource/FeatureSource"
|
|
||||||
import { OsmConnection } from "../Logic/Osm/OsmConnection"
|
import { OsmConnection } from "../Logic/Osm/OsmConnection"
|
||||||
import { ExportableMap, MapProperties } from "./MapProperties"
|
import { ExportableMap, MapProperties } from "./MapProperties"
|
||||||
import LayerState from "../Logic/State/LayerState"
|
import LayerState from "../Logic/State/LayerState"
|
||||||
|
@ -50,9 +46,7 @@ import BackgroundLayerResetter from "../Logic/Actors/BackgroundLayerResetter"
|
||||||
import SaveFeatureSourceToLocalStorage from "../Logic/FeatureSource/Actors/SaveFeatureSourceToLocalStorage"
|
import SaveFeatureSourceToLocalStorage from "../Logic/FeatureSource/Actors/SaveFeatureSourceToLocalStorage"
|
||||||
import BBoxFeatureSource from "../Logic/FeatureSource/Sources/TouchesBboxFeatureSource"
|
import BBoxFeatureSource from "../Logic/FeatureSource/Sources/TouchesBboxFeatureSource"
|
||||||
import ThemeViewStateHashActor from "../Logic/Web/ThemeViewStateHashActor"
|
import ThemeViewStateHashActor from "../Logic/Web/ThemeViewStateHashActor"
|
||||||
import NoElementsInViewDetector, {
|
import NoElementsInViewDetector, { FeatureViewState } from "../Logic/Actors/NoElementsInViewDetector"
|
||||||
FeatureViewState,
|
|
||||||
} from "../Logic/Actors/NoElementsInViewDetector"
|
|
||||||
import FilteredLayer from "./FilteredLayer"
|
import FilteredLayer from "./FilteredLayer"
|
||||||
import { PreferredRasterLayerSelector } from "../Logic/Actors/PreferredRasterLayerSelector"
|
import { PreferredRasterLayerSelector } from "../Logic/Actors/PreferredRasterLayerSelector"
|
||||||
import { ImageUploadManager } from "../Logic/ImageProviders/ImageUploadManager"
|
import { ImageUploadManager } from "../Logic/ImageProviders/ImageUploadManager"
|
||||||
|
@ -63,7 +57,7 @@ import { GeolocationControlState } from "../UI/BigComponents/GeolocationControl"
|
||||||
import Zoomcontrol from "../UI/Zoomcontrol"
|
import Zoomcontrol from "../UI/Zoomcontrol"
|
||||||
import {
|
import {
|
||||||
SummaryTileSource,
|
SummaryTileSource,
|
||||||
SummaryTileSourceRewriter,
|
SummaryTileSourceRewriter
|
||||||
} from "../Logic/FeatureSource/TiledFeatureSource/SummaryTileSource"
|
} from "../Logic/FeatureSource/TiledFeatureSource/SummaryTileSource"
|
||||||
import summaryLayer from "../assets/generated/layers/summary.json"
|
import summaryLayer from "../assets/generated/layers/summary.json"
|
||||||
import last_click_layerconfig from "../assets/generated/layers/last_click.json"
|
import last_click_layerconfig from "../assets/generated/layers/last_click.json"
|
||||||
|
@ -177,12 +171,6 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
)
|
)
|
||||||
this.map = new UIEventSource<MlMap>(undefined)
|
this.map = new UIEventSource<MlMap>(undefined)
|
||||||
const geolocationState = new GeoLocationState()
|
const geolocationState = new GeoLocationState()
|
||||||
const initial = new InitialMapPositioning(layout, geolocationState)
|
|
||||||
this.mapProperties = new MapLibreAdaptor(this.map, initial, { correctClick: 20 })
|
|
||||||
|
|
||||||
this.featureSwitchIsTesting = this.featureSwitches.featureSwitchIsTesting
|
|
||||||
this.featureSwitchUserbadge = this.featureSwitches.featureSwitchEnableLogin
|
|
||||||
|
|
||||||
this.osmConnection = new OsmConnection({
|
this.osmConnection = new OsmConnection({
|
||||||
dryRun: this.featureSwitches.featureSwitchIsTesting,
|
dryRun: this.featureSwitches.featureSwitchIsTesting,
|
||||||
fakeUser: this.featureSwitches.featureSwitchFakeUser.data,
|
fakeUser: this.featureSwitches.featureSwitchFakeUser.data,
|
||||||
|
@ -190,8 +178,15 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
"oauth_token",
|
"oauth_token",
|
||||||
undefined,
|
undefined,
|
||||||
"Used to complete the login"
|
"Used to complete the login"
|
||||||
),
|
)
|
||||||
})
|
})
|
||||||
|
const initial = new InitialMapPositioning(layout, geolocationState, this.osmConnection)
|
||||||
|
this.mapProperties = new MapLibreAdaptor(this.map, initial, { correctClick: 20 })
|
||||||
|
|
||||||
|
this.featureSwitchIsTesting = this.featureSwitches.featureSwitchIsTesting
|
||||||
|
this.featureSwitchUserbadge = this.featureSwitches.featureSwitchEnableLogin
|
||||||
|
|
||||||
|
|
||||||
this.userRelatedState = new UserRelatedState(
|
this.userRelatedState = new UserRelatedState(
|
||||||
this.osmConnection,
|
this.osmConnection,
|
||||||
layout,
|
layout,
|
||||||
|
@ -788,7 +783,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
|
|
||||||
const layers = this.theme.layers.filter(
|
const layers = this.theme.layers.filter(
|
||||||
(l) =>
|
(l) =>
|
||||||
Constants.priviliged_layers.indexOf(<any>l.id) < 0 &&
|
(<string[]><unknown>Constants.priviliged_layers).indexOf(l.id) < 0 &&
|
||||||
l.source.geojsonSource === undefined &&
|
l.source.geojsonSource === undefined &&
|
||||||
l.doCount
|
l.doCount
|
||||||
)
|
)
|
||||||
|
@ -840,7 +835,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
|
|
||||||
this.closestFeatures.registerSource(specialLayers.favourite, "favourite")
|
this.closestFeatures.registerSource(specialLayers.favourite, "favourite")
|
||||||
if (this.theme?.lockLocation) {
|
if (this.theme?.lockLocation) {
|
||||||
const bbox = new BBox(<any>this.theme.lockLocation)
|
const bbox = new BBox(<[[number, number], [number, number]]>this.theme.lockLocation)
|
||||||
this.mapProperties.maxbounds.setData(bbox)
|
this.mapProperties.maxbounds.setData(bbox)
|
||||||
ShowDataLayer.showRange(
|
ShowDataLayer.showRange(
|
||||||
this.map,
|
this.map,
|
||||||
|
|
Loading…
Reference in a new issue