forked from MapComplete/MapComplete
Merge master
This commit is contained in:
commit
214ba40dfa
66 changed files with 2226 additions and 1578 deletions
|
@ -84,7 +84,11 @@ export class SummaryTileSource extends DynamicTileSource {
|
|||
zoomRounded,
|
||||
0, // minzoom
|
||||
(tileIndex) => {
|
||||
const features = SummaryTileSource.downloadTile(tileIndex, cacheserver, layersSummed)
|
||||
const features = SummaryTileSource.downloadTile(
|
||||
tileIndex,
|
||||
cacheserver,
|
||||
layersSummed
|
||||
)
|
||||
const [z] = Tiles.tile_from_index(tileIndex)
|
||||
return new StaticFeatureSource(
|
||||
features.map(
|
||||
|
@ -103,7 +107,11 @@ export class SummaryTileSource extends DynamicTileSource {
|
|||
)
|
||||
}
|
||||
|
||||
public static downloadTile(tileIndex: number, cacheserver: string, layersSummed: string): Store<Feature<Point>[]>{
|
||||
public static downloadTile(
|
||||
tileIndex: number,
|
||||
cacheserver: string,
|
||||
layersSummed: string
|
||||
): Store<Feature<Point>[]> {
|
||||
const [z, x, y] = Tiles.tile_from_index(tileIndex)
|
||||
let coordinates = Tiles.centerPointOf(z, x, y)
|
||||
const url = `${cacheserver}/${layersSummed}/${z}/${x}/${y}.json`
|
||||
|
|
|
@ -14,7 +14,7 @@ export default class ChangeLocationAction extends OsmChangeAction {
|
|||
}[] = [
|
||||
{
|
||||
value: "relocated|improve_accuraccy|...",
|
||||
docs: "Will appear if the ",
|
||||
docs: "Will appear if the point has been moved",
|
||||
changeType: ["move"],
|
||||
specialMotivation: true,
|
||||
},
|
||||
|
|
|
@ -20,6 +20,19 @@ export default class DeleteAction extends OsmChangeAction {
|
|||
private readonly _id: OsmId
|
||||
private readonly _hardDelete: boolean
|
||||
|
||||
static metatags: {
|
||||
readonly key?: string
|
||||
readonly value?: string
|
||||
readonly docs: string
|
||||
readonly changeType: string[]
|
||||
readonly specialMotivation?: boolean
|
||||
}[] = [
|
||||
{
|
||||
docs: "Will appear if the object was deleted",
|
||||
changeType: ["deletion"],
|
||||
specialMotivation: true,
|
||||
},
|
||||
]
|
||||
constructor(
|
||||
id: OsmId,
|
||||
softDeletionTags: TagsFilter | undefined,
|
||||
|
|
|
@ -20,6 +20,7 @@ import Table from "../../UI/Base/Table"
|
|||
import ChangeLocationAction from "./Actions/ChangeLocationAction"
|
||||
import ChangeTagAction from "./Actions/ChangeTagAction"
|
||||
import FeatureSwitchState from "../State/FeatureSwitchState"
|
||||
import DeleteAction from "./Actions/DeleteAction"
|
||||
|
||||
/**
|
||||
* Handles all changes made to OSM.
|
||||
|
@ -55,7 +56,7 @@ export class Changes {
|
|||
featureSwitches?: FeatureSwitchState
|
||||
},
|
||||
leftRightSensitive: boolean = false,
|
||||
reportError?: (string: string | Error) => void
|
||||
reportError?: (string: string | Error) => void,
|
||||
) {
|
||||
this._leftRightSensitive = leftRightSensitive
|
||||
// We keep track of all changes just as well
|
||||
|
@ -70,7 +71,7 @@ export class Changes {
|
|||
state.osmConnection,
|
||||
state.featurePropertiesStore,
|
||||
this,
|
||||
(e) => this._reportError(e)
|
||||
(e) => this._reportError(e),
|
||||
)
|
||||
this.historicalUserLocations = state.historicalUserLocations
|
||||
|
||||
|
@ -84,7 +85,7 @@ export class Changes {
|
|||
modifiedObjects: OsmObject[]
|
||||
newObjects: OsmObject[]
|
||||
deletedObjects: OsmObject[]
|
||||
}
|
||||
},
|
||||
): string {
|
||||
const changedElements = allChanges.modifiedObjects ?? []
|
||||
const newElements = allChanges.newObjects ?? []
|
||||
|
@ -173,10 +174,11 @@ export class Changes {
|
|||
docs: "The identifier of the used background layer, this will probably be an identifier from the [editor layer index](https://github.com/osmlab/editor-layer-index)",
|
||||
},
|
||||
],
|
||||
"default"
|
||||
"default",
|
||||
),
|
||||
...addSource(ChangeTagAction.metatags, "ChangeTag"),
|
||||
...addSource(ChangeLocationAction.metatags, "ChangeLocation"),
|
||||
...addSource(DeleteAction.metatags, "DeleteAction")
|
||||
// TODO
|
||||
/*
|
||||
...DeleteAction.metatags,
|
||||
|
@ -201,7 +203,7 @@ export class Changes {
|
|||
: "",
|
||||
]),
|
||||
source,
|
||||
])
|
||||
]),
|
||||
),
|
||||
])
|
||||
}
|
||||
|
@ -250,7 +252,7 @@ export class Changes {
|
|||
const changeDescriptions = await action.Perform(this)
|
||||
changeDescriptions[0].meta.distanceToObject = this.calculateDistanceToChanges(
|
||||
action,
|
||||
changeDescriptions
|
||||
changeDescriptions,
|
||||
)
|
||||
this.applyChanges(changeDescriptions)
|
||||
}
|
||||
|
@ -264,7 +266,7 @@ export class Changes {
|
|||
|
||||
public CreateChangesetObjects(
|
||||
changes: ChangeDescription[],
|
||||
downloadedOsmObjects: OsmObject[]
|
||||
downloadedOsmObjects: OsmObject[],
|
||||
): {
|
||||
newObjects: OsmObject[]
|
||||
modifiedObjects: OsmObject[]
|
||||
|
@ -316,6 +318,8 @@ export class Changes {
|
|||
r.members = change.changes["members"]
|
||||
osmObj = r
|
||||
break
|
||||
default:
|
||||
throw "Got an invalid change.type: " + change.type
|
||||
}
|
||||
if (osmObj === undefined) {
|
||||
throw "Hmm? This is a bug"
|
||||
|
@ -424,14 +428,14 @@ export class Changes {
|
|||
result.modifiedObjects.length,
|
||||
"modified;",
|
||||
result.deletedObjects,
|
||||
"deleted"
|
||||
"deleted",
|
||||
)
|
||||
return result
|
||||
}
|
||||
|
||||
private calculateDistanceToChanges(
|
||||
change: OsmChangeAction,
|
||||
changeDescriptions: ChangeDescription[]
|
||||
changeDescriptions: ChangeDescription[],
|
||||
) {
|
||||
const locations = this.historicalUserLocations?.features?.data
|
||||
if (locations === undefined) {
|
||||
|
@ -451,7 +455,7 @@ export class Changes {
|
|||
.filter((feat) => feat.geometry.type === "Point")
|
||||
.filter((feat) => {
|
||||
const visitTime = new Date(
|
||||
(<GeoLocationPointProperties>(<any>feat.properties)).date
|
||||
(<GeoLocationPointProperties>(<any>feat.properties)).date,
|
||||
)
|
||||
// In seconds
|
||||
const diff = (now.getTime() - visitTime.getTime()) / 1000
|
||||
|
@ -498,42 +502,58 @@ export class Changes {
|
|||
...recentLocationPoints.map((gpsPoint) => {
|
||||
const otherCoor = GeoOperations.centerpointCoordinates(gpsPoint)
|
||||
return GeoOperations.distanceBetween(coor, otherCoor)
|
||||
})
|
||||
)
|
||||
)
|
||||
}),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload the selected changes to OSM.
|
||||
* Returns 'true' if successful and if they can be removed
|
||||
* Gets a single, fresh version of the requested osmObject with some error handling
|
||||
*/
|
||||
private async getOsmObject(id: string, downloader: OsmObjectDownloader) {
|
||||
try {
|
||||
try {
|
||||
|
||||
// Important: we do **not** cache this request, we _always_ need a fresh version!
|
||||
const osmObj = await downloader.DownloadObjectAsync(id, 0)
|
||||
return { id, osmObj }
|
||||
} catch (e) {
|
||||
const msg = "Could not download OSM-object" +
|
||||
id +
|
||||
" trying again before dropping it from the changes (" +
|
||||
e +
|
||||
")"
|
||||
this._reportError(msg)
|
||||
const osmObj = await downloader.DownloadObjectAsync(id, 0)
|
||||
return { id, osmObj }
|
||||
}
|
||||
} catch (e) {
|
||||
const msg = "Could not download OSM-object" +
|
||||
id +
|
||||
" dropping it from the changes (" +
|
||||
e +
|
||||
")"
|
||||
this._reportError(msg)
|
||||
this.errors.data.push(e)
|
||||
this.errors.ping()
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload the selected changes to OSM. This is typically called with changes for a single theme
|
||||
* @return pending changes which could not be uploaded for some reason; undefined or empty array if successful
|
||||
*/
|
||||
private async flushSelectChanges(
|
||||
pending: ChangeDescription[],
|
||||
openChangeset: UIEventSource<number>
|
||||
): Promise<boolean> {
|
||||
const self = this
|
||||
openChangeset: UIEventSource<number>,
|
||||
): Promise<ChangeDescription[]> {
|
||||
const neededIds = Changes.GetNeededIds(pending)
|
||||
// We _do not_ pass in the Changes object itself - we want the data from OSM directly in order to apply the changes
|
||||
const downloader = new OsmObjectDownloader(this.backend, undefined)
|
||||
let osmObjects = await Promise.all<{ id: string; osmObj: OsmObject | "deleted" }>(
|
||||
neededIds.map(async (id) => {
|
||||
try {
|
||||
// Important: we do **not** cache this request, we _always_ need a fresh version!
|
||||
const osmObj = await downloader.DownloadObjectAsync(id, 0)
|
||||
return { id, osmObj }
|
||||
} catch (e) {
|
||||
const msg = "Could not download OSM-object" +
|
||||
id +
|
||||
" dropping it from the changes (" +
|
||||
e +
|
||||
")"
|
||||
this._reportError(msg)
|
||||
this.errors.data.push(e)
|
||||
this.errors.ping()
|
||||
return undefined
|
||||
}
|
||||
})
|
||||
neededIds.map(id => this.getOsmObject(id, downloader)),
|
||||
)
|
||||
|
||||
osmObjects = Utils.NoNull(osmObjects)
|
||||
|
@ -554,7 +574,7 @@ export class Changes {
|
|||
|
||||
if (pending.length == 0) {
|
||||
console.log("No pending changes...")
|
||||
return true
|
||||
return undefined
|
||||
}
|
||||
|
||||
const perType = Array.from(
|
||||
|
@ -562,15 +582,15 @@ export class Changes {
|
|||
pending
|
||||
.filter(
|
||||
(descr) =>
|
||||
descr.meta.changeType !== undefined && descr.meta.changeType !== null
|
||||
descr.meta.changeType !== undefined && descr.meta.changeType !== null,
|
||||
)
|
||||
.map((descr) => descr.meta.changeType)
|
||||
.map((descr) => descr.meta.changeType),
|
||||
),
|
||||
([key, count]) => ({
|
||||
key: key,
|
||||
value: count,
|
||||
aggregate: true,
|
||||
})
|
||||
}),
|
||||
)
|
||||
const motivations = pending
|
||||
.filter((descr) => descr.meta.specialMotivation !== undefined)
|
||||
|
@ -581,7 +601,7 @@ export class Changes {
|
|||
|
||||
const distances = Utils.NoNull(pending.map((descr) => descr.meta.distanceToObject))
|
||||
distances.sort((a, b) => a - b)
|
||||
const perBinCount = Constants.distanceToChangeObjectBins.map((_) => 0)
|
||||
const perBinCount = Constants.distanceToChangeObjectBins.map(() => 0)
|
||||
|
||||
let j = 0
|
||||
const maxDistances = Constants.distanceToChangeObjectBins
|
||||
|
@ -609,7 +629,7 @@ export class Changes {
|
|||
value: count,
|
||||
aggregate: true,
|
||||
}
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
// This method is only called with changedescriptions for this theme
|
||||
|
@ -633,26 +653,42 @@ export class Changes {
|
|||
...perBinMessage,
|
||||
]
|
||||
|
||||
const refused: ChangeDescription[] = []
|
||||
let toUpload: ChangeDescription[] = []
|
||||
|
||||
pending.forEach(c => {
|
||||
if (c.id < 0) {
|
||||
toUpload.push(c)
|
||||
return
|
||||
}
|
||||
const matchFound = !!objects.find(o => o.id === c.id && o.type === c.type)
|
||||
if (matchFound) {
|
||||
toUpload.push(c)
|
||||
} else {
|
||||
refused.push(c)
|
||||
}
|
||||
})
|
||||
|
||||
await this._changesetHandler.UploadChangeset(
|
||||
(csId, remappings) => {
|
||||
if (remappings.size > 0) {
|
||||
pending = pending.map((ch) => ChangeDescriptionTools.rewriteIds(ch, remappings))
|
||||
toUpload = toUpload.map((ch) => ChangeDescriptionTools.rewriteIds(ch, remappings))
|
||||
}
|
||||
|
||||
const changes: {
|
||||
newObjects: OsmObject[]
|
||||
modifiedObjects: OsmObject[]
|
||||
deletedObjects: OsmObject[]
|
||||
} = self.CreateChangesetObjects(pending, objects)
|
||||
} = this.CreateChangesetObjects(toUpload, objects)
|
||||
|
||||
return Changes.createChangesetFor("" + csId, changes)
|
||||
},
|
||||
metatags,
|
||||
openChangeset
|
||||
openChangeset,
|
||||
)
|
||||
|
||||
console.log("Upload successful!")
|
||||
return true
|
||||
console.log("Upload successful! Refused changes are",refused)
|
||||
return refused
|
||||
}
|
||||
|
||||
private async flushChangesAsync(): Promise<void> {
|
||||
|
@ -670,44 +706,42 @@ export class Changes {
|
|||
pendingPerTheme.get(theme).push(changeDescription)
|
||||
}
|
||||
|
||||
const successes = await Promise.all(
|
||||
const refusedChanges: ChangeDescription[][] = await Promise.all(
|
||||
Array.from(pendingPerTheme, async ([theme, pendingChanges]) => {
|
||||
try {
|
||||
const openChangeset = UIEventSource.asInt(
|
||||
this.state.osmConnection.GetPreference(
|
||||
"current-open-changeset-" + theme
|
||||
)
|
||||
"current-open-changeset-" + theme,
|
||||
),
|
||||
)
|
||||
console.log(
|
||||
"Using current-open-changeset-" +
|
||||
theme +
|
||||
" from the preferences, got " +
|
||||
openChangeset.data
|
||||
theme +
|
||||
" from the preferences, got " +
|
||||
openChangeset.data,
|
||||
)
|
||||
|
||||
const result = await self.flushSelectChanges(pendingChanges, openChangeset)
|
||||
if (result) {
|
||||
const refused = await self.flushSelectChanges(pendingChanges, openChangeset)
|
||||
if (!refused) {
|
||||
this.errors.setData([])
|
||||
}
|
||||
return result
|
||||
return refused
|
||||
} catch (e) {
|
||||
this._reportError(e)
|
||||
console.error("Could not upload some changes:", e)
|
||||
this.errors.data.push(e)
|
||||
this.errors.ping()
|
||||
return false
|
||||
return pendingChanges
|
||||
}
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
if (!successes.some((s) => s == false)) {
|
||||
// All changes successfull, we clear the data!
|
||||
this.pendingChanges.setData([])
|
||||
}
|
||||
// We keep all the refused changes to try them again
|
||||
this.pendingChanges.setData(refusedChanges.flatMap(c => c))
|
||||
} catch (e) {
|
||||
console.error(
|
||||
"Could not handle changes - probably an old, pending changeset in localstorage with an invalid format; erasing those",
|
||||
e
|
||||
e,
|
||||
)
|
||||
this.errors.data.push(e)
|
||||
this.errors.ping()
|
||||
|
|
|
@ -154,7 +154,7 @@ export class ChangesetHandler {
|
|||
if (this._reportError) {
|
||||
this._reportError(e)
|
||||
}
|
||||
console.warn("Could not open/upload changeset due to ", e, "trying t")
|
||||
console.warn("Could not open/upload changeset due to ", e, "trying again with a another fresh changeset ")
|
||||
openChangeset.setData(undefined)
|
||||
|
||||
throw e
|
||||
|
@ -187,7 +187,7 @@ export class ChangesetHandler {
|
|||
await this.UpdateTags(csId, rewrittenTags)
|
||||
} catch (e) {
|
||||
if (this._reportError) {
|
||||
this._reportError(e)
|
||||
this._reportError("Could not reuse changeset, might be closed: " + e.stacktrace ?? "" + e)
|
||||
}
|
||||
console.warn("Could not upload, changeset is probably closed: ", e)
|
||||
openChangeset.setData(undefined)
|
||||
|
|
|
@ -18,14 +18,14 @@ class FeatureSwitchUtils {
|
|||
key,
|
||||
"" + defaultValue,
|
||||
documentation,
|
||||
{ stackOffset: -1 },
|
||||
{ stackOffset: -1 }
|
||||
)
|
||||
|
||||
// It takes the current layout, extracts the default value for this query parameter. A query parameter event source is then retrieved and flattened
|
||||
return queryParam.sync(
|
||||
(str) => (str === undefined ? defaultValue : str !== "false"),
|
||||
[],
|
||||
(b) => (b == defaultValue ? undefined : "" + b),
|
||||
(b) => (b == defaultValue ? undefined : "" + b)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ export class OsmConnectionFeatureSwitches {
|
|||
this.featureSwitchFakeUser = QueryParameters.GetBooleanQueryParameter(
|
||||
"fake-user",
|
||||
false,
|
||||
"If true, 'dryrun' mode is activated and a fake user account is loaded",
|
||||
"If true, 'dryrun' mode is activated and a fake user account is loaded"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -99,14 +99,14 @@ export default class FeatureSwitchState extends OsmConnectionFeatureSwitches {
|
|||
this.featureSwitchEnableLogin = FeatureSwitchUtils.initSwitch(
|
||||
"fs-enable-login",
|
||||
layoutToUse?.enableUserBadge ?? true,
|
||||
"Disables/Enables logging in and thus disables editing all together. This effectively puts MapComplete into read-only mode.",
|
||||
"Disables/Enables logging in and thus disables editing all together. This effectively puts MapComplete into read-only mode."
|
||||
)
|
||||
{
|
||||
if (QueryParameters.wasInitialized("fs-userbadge")) {
|
||||
// userbadge is the legacy name for 'enable-login'
|
||||
this.featureSwitchEnableLogin.setData(
|
||||
QueryParameters.GetBooleanQueryParameter("fs-userbadge", undefined, "Legacy")
|
||||
.data,
|
||||
.data
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -114,64 +114,66 @@ export default class FeatureSwitchState extends OsmConnectionFeatureSwitches {
|
|||
this.featureSwitchSearch = FeatureSwitchUtils.initSwitch(
|
||||
"fs-search",
|
||||
layoutToUse?.enableSearch ?? true,
|
||||
"Disables/Enables the search bar",
|
||||
"Disables/Enables the search bar"
|
||||
)
|
||||
this.featureSwitchBackgroundSelection = FeatureSwitchUtils.initSwitch(
|
||||
"fs-background",
|
||||
layoutToUse?.enableBackgroundLayerSelection ?? true,
|
||||
"Disables/Enables the background layer control where a user can enable e.g. aerial imagery",
|
||||
"Disables/Enables the background layer control where a user can enable e.g. aerial imagery"
|
||||
)
|
||||
|
||||
this.featureSwitchFilter = FeatureSwitchUtils.initSwitch(
|
||||
"fs-filter",
|
||||
layoutToUse?.enableLayers ?? true,
|
||||
"Disables/Enables the filter view where a user can enable/disable MapComplete-layers or filter for certain properties",
|
||||
"Disables/Enables the filter view where a user can enable/disable MapComplete-layers or filter for certain properties"
|
||||
)
|
||||
|
||||
this.featureSwitchWelcomeMessage = FeatureSwitchUtils.initSwitch(
|
||||
"fs-welcome-message",
|
||||
true,
|
||||
"Disables/enables the help menu or welcome message",
|
||||
"Disables/enables the help menu or welcome message"
|
||||
)
|
||||
this.featureSwitchCommunityIndex = FeatureSwitchUtils.initSwitch(
|
||||
"fs-community-index",
|
||||
this.featureSwitchEnableLogin.data,
|
||||
"Disables/enables the button to get in touch with the community",
|
||||
"Disables/enables the button to get in touch with the community"
|
||||
)
|
||||
this.featureSwitchExtraLinkEnabled = FeatureSwitchUtils.initSwitch(
|
||||
"fs-iframe-popout",
|
||||
true,
|
||||
"Disables/Enables the extraLink button. By default, if in iframe mode and the welcome message is hidden, a popout button to the full mapcomplete instance is shown instead (unless disabled with this switch or another extraLink button is enabled)",
|
||||
"Disables/Enables the extraLink button. By default, if in iframe mode and the welcome message is hidden, a popout button to the full mapcomplete instance is shown instead (unless disabled with this switch or another extraLink button is enabled)"
|
||||
)
|
||||
this.featureSwitchBackToThemeOverview = FeatureSwitchUtils.initSwitch(
|
||||
"fs-homepage-link",
|
||||
layoutToUse?.enableMoreQuests ?? true,
|
||||
"Disables/Enables the various links which go back to the index page with the theme overview",
|
||||
"Disables/Enables the various links which go back to the index page with the theme overview"
|
||||
)
|
||||
this.featureSwitchShareScreen = FeatureSwitchUtils.initSwitch(
|
||||
"fs-share-screen",
|
||||
layoutToUse?.enableShareScreen ?? true,
|
||||
"Disables/Enables the 'Share-screen'-tab in the welcome message",
|
||||
"Disables/Enables the 'Share-screen'-tab in the welcome message"
|
||||
)
|
||||
this.featureSwitchGeolocation = FeatureSwitchUtils.initSwitch(
|
||||
"fs-geolocation",
|
||||
layoutToUse?.enableGeolocation ?? true,
|
||||
"Disables/Enables the geolocation button",
|
||||
"Disables/Enables the geolocation button"
|
||||
)
|
||||
|
||||
this.featureSwitchLayerDefault = QueryParameters.GetBooleanQueryParameter("fs-layers-enabled", true,
|
||||
"If set to false, all layers will be disabled - except the explicitly enabled layers",
|
||||
this.featureSwitchLayerDefault = QueryParameters.GetBooleanQueryParameter(
|
||||
"fs-layers-enabled",
|
||||
true,
|
||||
"If set to false, all layers will be disabled - except the explicitly enabled layers"
|
||||
)
|
||||
this.featureSwitchShowAllQuestions = FeatureSwitchUtils.initSwitch(
|
||||
"fs-all-questions",
|
||||
layoutToUse?.enableShowAllQuestions ?? false,
|
||||
"Always show all questions",
|
||||
"Always show all questions"
|
||||
)
|
||||
|
||||
this.featureSwitchEnableExport = FeatureSwitchUtils.initSwitch(
|
||||
"fs-export",
|
||||
layoutToUse?.enableExportButton ?? true,
|
||||
"Enable the export as GeoJSON and CSV button",
|
||||
"Enable the export as GeoJSON and CSV button"
|
||||
)
|
||||
|
||||
let testingDefaultValue = false
|
||||
|
@ -185,60 +187,59 @@ export default class FeatureSwitchState extends OsmConnectionFeatureSwitches {
|
|||
this.featureSwitchIsTesting = QueryParameters.GetBooleanQueryParameter(
|
||||
"test",
|
||||
testingDefaultValue,
|
||||
"If true, 'dryrun' mode is activated. The app will behave as normal, except that changes to OSM will be printed onto the console instead of actually uploaded to osm.org",
|
||||
"If true, 'dryrun' mode is activated. The app will behave as normal, except that changes to OSM will be printed onto the console instead of actually uploaded to osm.org"
|
||||
)
|
||||
|
||||
this.featureSwitchIsDebugging = QueryParameters.GetBooleanQueryParameter(
|
||||
"debug",
|
||||
false,
|
||||
"If true, shows some extra debugging help such as all the available tags on every object",
|
||||
"If true, shows some extra debugging help such as all the available tags on every object"
|
||||
)
|
||||
|
||||
this.featureSwitchMorePrivacy = QueryParameters.GetBooleanQueryParameter(
|
||||
"moreprivacy",
|
||||
layoutToUse.enableMorePrivacy,
|
||||
"If true, the location distance indication will not be written to the changeset and other privacy enhancing measures might be taken.",
|
||||
"If true, the location distance indication will not be written to the changeset and other privacy enhancing measures might be taken."
|
||||
)
|
||||
|
||||
this.overpassUrl = QueryParameters.GetQueryParameter(
|
||||
"overpassUrl",
|
||||
(layoutToUse?.overpassUrl ?? Constants.defaultOverpassUrls).join(","),
|
||||
"Point mapcomplete to a different overpass-instance. Example: https://overpass-api.de/api/interpreter",
|
||||
"Point mapcomplete to a different overpass-instance. Example: https://overpass-api.de/api/interpreter"
|
||||
).sync(
|
||||
(param) => param?.split(","),
|
||||
[],
|
||||
(urls) => urls?.join(","),
|
||||
(urls) => urls?.join(",")
|
||||
)
|
||||
|
||||
this.overpassTimeout = UIEventSource.asInt(
|
||||
QueryParameters.GetQueryParameter(
|
||||
"overpassTimeout",
|
||||
"" + layoutToUse?.overpassTimeout,
|
||||
"Set a different timeout (in seconds) for queries in overpass",
|
||||
),
|
||||
"Set a different timeout (in seconds) for queries in overpass"
|
||||
)
|
||||
)
|
||||
|
||||
this.overpassMaxZoom = UIEventSource.asFloat(
|
||||
QueryParameters.GetQueryParameter(
|
||||
"overpassMaxZoom",
|
||||
"" + layoutToUse?.overpassMaxZoom,
|
||||
" point to switch between OSM-api and overpass",
|
||||
),
|
||||
" point to switch between OSM-api and overpass"
|
||||
)
|
||||
)
|
||||
|
||||
this.osmApiTileSize = UIEventSource.asInt(
|
||||
QueryParameters.GetQueryParameter(
|
||||
"osmApiTileSize",
|
||||
"" + layoutToUse?.osmApiTileSize,
|
||||
"Tilesize when the OSM-API is used to fetch data within a BBOX",
|
||||
),
|
||||
"Tilesize when the OSM-API is used to fetch data within a BBOX"
|
||||
)
|
||||
)
|
||||
|
||||
this.backgroundLayerId = QueryParameters.GetQueryParameter(
|
||||
"background",
|
||||
layoutToUse?.defaultBackgroundId,
|
||||
"The id of the background layer to start with",
|
||||
"The id of the background layer to start with"
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,13 +35,23 @@ export default class LayerState {
|
|||
* @param context
|
||||
* @param layersEnabledByDefault
|
||||
*/
|
||||
constructor(osmConnection: OsmConnection, layers: LayerConfig[], context: string, layersEnabledByDefault: Store<boolean>) {
|
||||
constructor(
|
||||
osmConnection: OsmConnection,
|
||||
layers: LayerConfig[],
|
||||
context: string,
|
||||
layersEnabledByDefault: Store<boolean>
|
||||
) {
|
||||
this.osmConnection = osmConnection
|
||||
const filteredLayers = new Map()
|
||||
for (const layer of layers) {
|
||||
filteredLayers.set(
|
||||
layer.id,
|
||||
FilteredLayer.initLinkedState(layer, context, this.osmConnection, layersEnabledByDefault)
|
||||
FilteredLayer.initLinkedState(
|
||||
layer,
|
||||
context,
|
||||
this.osmConnection,
|
||||
layersEnabledByDefault
|
||||
)
|
||||
)
|
||||
}
|
||||
this.filteredLayers = filteredLayers
|
||||
|
|
|
@ -1,14 +1,42 @@
|
|||
import { Utils } from "../../Utils"
|
||||
/** This code is autogenerated - do not edit. Edit ./assets/layers/usersettings/usersettings.json instead */
|
||||
export class ThemeMetaTagging {
|
||||
public static readonly themeName = "usersettings"
|
||||
public static readonly themeName = "usersettings"
|
||||
|
||||
public metaTaggging_for_usersettings(feat: {properties: Record<string, string>}) {
|
||||
Utils.AddLazyProperty(feat.properties, '_mastodon_candidate_md', () => feat.properties._description.match(/\[[^\]]*\]\((.*(mastodon|en.osm.town).*)\).*/)?.at(1) )
|
||||
Utils.AddLazyProperty(feat.properties, '_d', () => feat.properties._description?.replace(/</g,'<')?.replace(/>/g,'>') ?? '' )
|
||||
Utils.AddLazyProperty(feat.properties, '_mastodon_candidate_a', () => (feat => {const e = document.createElement('div');e.innerHTML = feat.properties._d;return Array.from(e.getElementsByTagName("a")).filter(a => a.href.match(/mastodon|en.osm.town/) !== null)[0]?.href }) (feat) )
|
||||
Utils.AddLazyProperty(feat.properties, '_mastodon_link', () => (feat => {const e = document.createElement('div');e.innerHTML = feat.properties._d;return Array.from(e.getElementsByTagName("a")).filter(a => a.getAttribute("rel")?.indexOf('me') >= 0)[0]?.href})(feat) )
|
||||
Utils.AddLazyProperty(feat.properties, '_mastodon_candidate', () => feat.properties._mastodon_candidate_md ?? feat.properties._mastodon_candidate_a )
|
||||
feat.properties['__current_backgroun'] = 'initial_value'
|
||||
}
|
||||
}
|
||||
public metaTaggging_for_usersettings(feat: { properties: Record<string, string> }) {
|
||||
Utils.AddLazyProperty(feat.properties, "_mastodon_candidate_md", () =>
|
||||
feat.properties._description
|
||||
.match(/\[[^\]]*\]\((.*(mastodon|en.osm.town).*)\).*/)
|
||||
?.at(1)
|
||||
)
|
||||
Utils.AddLazyProperty(
|
||||
feat.properties,
|
||||
"_d",
|
||||
() => feat.properties._description?.replace(/</g, "<")?.replace(/>/g, ">") ?? ""
|
||||
)
|
||||
Utils.AddLazyProperty(feat.properties, "_mastodon_candidate_a", () =>
|
||||
((feat) => {
|
||||
const e = document.createElement("div")
|
||||
e.innerHTML = feat.properties._d
|
||||
return Array.from(e.getElementsByTagName("a")).filter(
|
||||
(a) => a.href.match(/mastodon|en.osm.town/) !== null
|
||||
)[0]?.href
|
||||
})(feat)
|
||||
)
|
||||
Utils.AddLazyProperty(feat.properties, "_mastodon_link", () =>
|
||||
((feat) => {
|
||||
const e = document.createElement("div")
|
||||
e.innerHTML = feat.properties._d
|
||||
return Array.from(e.getElementsByTagName("a")).filter(
|
||||
(a) => a.getAttribute("rel")?.indexOf("me") >= 0
|
||||
)[0]?.href
|
||||
})(feat)
|
||||
)
|
||||
Utils.AddLazyProperty(
|
||||
feat.properties,
|
||||
"_mastodon_candidate",
|
||||
() => feat.properties._mastodon_candidate_md ?? feat.properties._mastodon_candidate_a
|
||||
)
|
||||
feat.properties["__current_backgroun"] = "initial_value"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -169,7 +169,17 @@ export default class Constants {
|
|||
public static readonly maptilerApiKey = "GvoVAJgu46I5rZapJuAy"
|
||||
public static readonly SummaryServer: string = Constants.config.summary_server
|
||||
|
||||
public static allServers: string[] = [Constants.SummaryServer, Constants.VectorTileServer, Constants.GeoIpServer, Constants.ErrorReportServer, Constants.countryCoderEndpoint, Constants.osmAuthConfig.url, Constants.nominatimEndpoint, Constants.linkedDataProxy, ...Constants.defaultOverpassUrls]
|
||||
public static allServers: string[] = [
|
||||
Constants.SummaryServer,
|
||||
Constants.VectorTileServer,
|
||||
Constants.GeoIpServer,
|
||||
Constants.ErrorReportServer,
|
||||
Constants.countryCoderEndpoint,
|
||||
Constants.osmAuthConfig.url,
|
||||
Constants.nominatimEndpoint,
|
||||
Constants.linkedDataProxy,
|
||||
...Constants.defaultOverpassUrls,
|
||||
]
|
||||
|
||||
private static isRetina(): boolean {
|
||||
if (Utils.runningFromConsole) {
|
||||
|
|
|
@ -104,12 +104,12 @@ export default class FilteredLayer {
|
|||
)
|
||||
} else {
|
||||
let isShown = layer.shownByDefault
|
||||
if(enabledByDefault !== undefined && enabledByDefault.data === false){
|
||||
if (enabledByDefault !== undefined && enabledByDefault.data === false) {
|
||||
isShown = false
|
||||
}
|
||||
isDisplayed = QueryParameters.GetBooleanQueryParameter(
|
||||
FilteredLayer.queryParameterKey(layer),
|
||||
isShown ,
|
||||
isShown,
|
||||
"Whether or not layer " + layer.id + " is shown"
|
||||
)
|
||||
}
|
||||
|
|
|
@ -39,7 +39,9 @@ export class MenuState {
|
|||
/**
|
||||
* Standalone copyright panel
|
||||
*/
|
||||
public readonly copyrightPanelIsOpened: UIEventSource<boolean> = new UIEventSource<boolean>(false)
|
||||
public readonly copyrightPanelIsOpened: UIEventSource<boolean> = new UIEventSource<boolean>(
|
||||
false
|
||||
)
|
||||
|
||||
public readonly communityIndexPanelIsOpened: UIEventSource<boolean> = new UIEventSource(false)
|
||||
public readonly allToggles: {
|
||||
|
@ -140,7 +142,6 @@ export class MenuState {
|
|||
name: "background",
|
||||
showOverOthers: true,
|
||||
},
|
||||
|
||||
]
|
||||
for (const toggle of this.allToggles) {
|
||||
toggle.toggle.addCallback((isOpen) => {
|
||||
|
|
|
@ -336,13 +336,12 @@ export default class LayoutConfig implements LayoutInformation {
|
|||
...json,
|
||||
layers: json.layers.filter((l) => l["id"] !== "favourite"),
|
||||
}
|
||||
const usedImages =
|
||||
new ExtractImages(this.official, undefined)
|
||||
.convertStrict(
|
||||
jsonNoFavourites,
|
||||
ConversionContext.construct([json.id], ["ExtractImages"])
|
||||
)
|
||||
.flatMap((i) => i.path)
|
||||
const usedImages = new ExtractImages(this.official, undefined)
|
||||
.convertStrict(
|
||||
jsonNoFavourites,
|
||||
ConversionContext.construct([json.id], ["ExtractImages"])
|
||||
)
|
||||
.flatMap((i) => i.path)
|
||||
usedImages.sort()
|
||||
|
||||
this.usedImages = Utils.Dedup(usedImages)
|
||||
|
|
|
@ -158,7 +158,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
|||
this.featureSwitches = new FeatureSwitchState(layout)
|
||||
this.guistate = new MenuState(
|
||||
this.featureSwitches.featureSwitchWelcomeMessage.data,
|
||||
layout.id,
|
||||
layout.id
|
||||
)
|
||||
this.map = new UIEventSource<MlMap>(undefined)
|
||||
const geolocationState = new GeoLocationState()
|
||||
|
@ -174,14 +174,14 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
|||
oauth_token: QueryParameters.GetQueryParameter(
|
||||
"oauth_token",
|
||||
undefined,
|
||||
"Used to complete the login",
|
||||
"Used to complete the login"
|
||||
),
|
||||
})
|
||||
this.userRelatedState = new UserRelatedState(
|
||||
this.osmConnection,
|
||||
layout,
|
||||
this.featureSwitches,
|
||||
this.mapProperties,
|
||||
this.mapProperties
|
||||
)
|
||||
this.userRelatedState.fixateNorth.addCallbackAndRunD((fixated) => {
|
||||
this.mapProperties.allowRotating.setData(fixated !== "yes")
|
||||
|
@ -192,17 +192,22 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
|||
geolocationState,
|
||||
this.selectedElement,
|
||||
this.mapProperties,
|
||||
this.userRelatedState.gpsLocationHistoryRetentionTime,
|
||||
this.userRelatedState.gpsLocationHistoryRetentionTime
|
||||
)
|
||||
this.geolocationControl = new GeolocationControlState(this.geolocation, this.mapProperties)
|
||||
|
||||
this.availableLayers = AvailableRasterLayers.layersAvailableAt(
|
||||
this.mapProperties.location,
|
||||
this.osmConnection.isLoggedIn,
|
||||
this.osmConnection.isLoggedIn
|
||||
)
|
||||
|
||||
const self = this
|
||||
this.layerState = new LayerState(this.osmConnection, layout.layers, layout.id, this.featureSwitches.featureSwitchLayerDefault)
|
||||
this.layerState = new LayerState(
|
||||
this.osmConnection,
|
||||
layout.layers,
|
||||
layout.id,
|
||||
this.featureSwitches.featureSwitchLayerDefault
|
||||
)
|
||||
|
||||
{
|
||||
const overlayLayerStates = new Map<string, { isDisplayed: UIEventSource<boolean> }>()
|
||||
|
@ -210,7 +215,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
|||
const isDisplayed = QueryParameters.GetBooleanQueryParameter(
|
||||
"overlay-" + rasterInfo.id,
|
||||
rasterInfo.defaultState ?? true,
|
||||
"Whether or not overlay layer " + rasterInfo.id + " is shown",
|
||||
"Whether or not overlay layer " + rasterInfo.id + " is shown"
|
||||
)
|
||||
const state = { isDisplayed }
|
||||
overlayLayerStates.set(rasterInfo.id, state)
|
||||
|
@ -235,7 +240,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
|||
this.osmConnection.Backend(),
|
||||
(id) => self.layerState.filteredLayers.get(id).isDisplayed,
|
||||
mvtAvailableLayers,
|
||||
this.fullNodeDatabase,
|
||||
this.fullNodeDatabase
|
||||
)
|
||||
|
||||
let currentViewIndex = 0
|
||||
|
@ -253,7 +258,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
|||
id: "current_view_" + currentViewIndex,
|
||||
}),
|
||||
]
|
||||
}),
|
||||
})
|
||||
)
|
||||
this.featuresInView = new BBoxFeatureSource(layoutSource, this.mapProperties.bounds)
|
||||
|
||||
|
@ -271,19 +276,19 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
|||
featureSwitches: this.featureSwitches,
|
||||
},
|
||||
layout?.isLeftRightSensitive() ?? false,
|
||||
(e) => this.reportError(e),
|
||||
(e) => this.reportError(e)
|
||||
)
|
||||
this.historicalUserLocations = this.geolocation.historicalUserLocations
|
||||
this.newFeatures = new NewGeometryFromChangesFeatureSource(
|
||||
this.changes,
|
||||
layoutSource,
|
||||
this.featureProperties,
|
||||
this.featureProperties
|
||||
)
|
||||
layoutSource.addSource(this.newFeatures)
|
||||
|
||||
const perLayer = new PerLayerFeatureSourceSplitter(
|
||||
Array.from(this.layerState.filteredLayers.values()).filter(
|
||||
(l) => l.layerDef?.source !== null,
|
||||
(l) => l.layerDef?.source !== null
|
||||
),
|
||||
new ChangeGeometryApplicator(this.indexedFeatures, this.changes),
|
||||
{
|
||||
|
@ -294,10 +299,10 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
|||
"Got ",
|
||||
features.length,
|
||||
"leftover features, such as",
|
||||
features[0].properties,
|
||||
features[0].properties
|
||||
)
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
this.perLayer = perLayer.perLayer
|
||||
}
|
||||
|
@ -337,12 +342,12 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
|||
this.lastClickObject = new LastClickFeatureSource(
|
||||
this.layout,
|
||||
this.mapProperties.lastClickLocation,
|
||||
this.userRelatedState.addNewFeatureMode,
|
||||
this.userRelatedState.addNewFeatureMode
|
||||
)
|
||||
|
||||
this.osmObjectDownloader = new OsmObjectDownloader(
|
||||
this.osmConnection.Backend(),
|
||||
this.changes,
|
||||
this.changes
|
||||
)
|
||||
|
||||
this.perLayerFiltered = this.showNormalDataOn(this.map)
|
||||
|
@ -353,7 +358,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
|||
currentZoom: this.mapProperties.zoom,
|
||||
layerState: this.layerState,
|
||||
bounds: this.visualFeedbackViewportBounds,
|
||||
},
|
||||
}
|
||||
)
|
||||
this.hasDataInView = new NoElementsInViewDetector(this).hasFeatureInView
|
||||
this.imageUploadManager = new ImageUploadManager(
|
||||
|
@ -361,11 +366,13 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
|||
Imgur.singleton,
|
||||
this.featureProperties,
|
||||
this.osmConnection,
|
||||
this.changes,
|
||||
this.changes
|
||||
)
|
||||
this.favourites = new FavouritesFeatureSource(this)
|
||||
|
||||
this.featureSummary = this.setupSummaryLayer(new LayerConfig(<LayerConfigJson>summaryLayer, "summaryLayer", true))
|
||||
this.featureSummary = this.setupSummaryLayer(
|
||||
new LayerConfig(<LayerConfigJson>summaryLayer, "summaryLayer", true)
|
||||
)
|
||||
this.toCacheSavers = this.initSaveToLocalStorage()
|
||||
this.initActors()
|
||||
this.drawSpecialLayers()
|
||||
|
@ -404,7 +411,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
|||
LayoutSource.fromCacheZoomLevel,
|
||||
fs,
|
||||
this.featureProperties,
|
||||
fs.layer.layerDef.maxAgeOfCache,
|
||||
fs.layer.layerDef.maxAgeOfCache
|
||||
)
|
||||
toLocalStorage.set(layerId, storage)
|
||||
})
|
||||
|
@ -417,7 +424,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
|||
const doShowLayer = this.mapProperties.zoom.map(
|
||||
(z) =>
|
||||
(fs.layer.isDisplayed?.data ?? true) && z >= (fs.layer.layerDef?.minzoom ?? 0),
|
||||
[fs.layer.isDisplayed],
|
||||
[fs.layer.isDisplayed]
|
||||
)
|
||||
|
||||
if (!doShowLayer.data && this.featureSwitches.featureSwitchFilter.data === false) {
|
||||
|
@ -434,7 +441,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
|||
fs.layer,
|
||||
fs,
|
||||
(id) => this.featureProperties.getStore(id),
|
||||
this.layerState.globalFilters,
|
||||
this.layerState.globalFilters
|
||||
)
|
||||
filteringFeatureSource.set(layerName, filtered)
|
||||
|
||||
|
@ -575,7 +582,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
|||
return
|
||||
}
|
||||
this.selectClosestAtCenter(0)
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
for (let i = 1; i < 9; i++) {
|
||||
|
@ -593,7 +600,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
|||
onUp: true,
|
||||
},
|
||||
doc,
|
||||
() => this.selectClosestAtCenter(i - 1),
|
||||
() => this.selectClosestAtCenter(i - 1)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -610,7 +617,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
|||
if (this.featureSwitches.featureSwitchBackgroundSelection.data) {
|
||||
this.guistate.backgroundLayerSelectionIsOpened.setData(true)
|
||||
}
|
||||
},
|
||||
}
|
||||
)
|
||||
Hotkeys.RegisterHotkey(
|
||||
{
|
||||
|
@ -622,14 +629,14 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
|||
if (this.featureSwitches.featureSwitchFilter.data) {
|
||||
this.guistate.openFilterView()
|
||||
}
|
||||
},
|
||||
}
|
||||
)
|
||||
Hotkeys.RegisterHotkey(
|
||||
{ shift: "O" },
|
||||
Translations.t.hotkeyDocumentation.selectMapnik,
|
||||
() => {
|
||||
this.mapProperties.rasterLayer.setData(AvailableRasterLayers.osmCarto)
|
||||
},
|
||||
}
|
||||
)
|
||||
const setLayerCategory = (category: EliCategory) => {
|
||||
const available = this.availableLayers.data
|
||||
|
@ -637,7 +644,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
|||
const best = RasterLayerUtils.SelectBestLayerAccordingTo(
|
||||
available,
|
||||
category,
|
||||
current.data,
|
||||
current.data
|
||||
)
|
||||
console.log("Best layer for category", category, "is", best.properties.id)
|
||||
current.setData(best)
|
||||
|
@ -646,26 +653,26 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
|||
Hotkeys.RegisterHotkey(
|
||||
{ nomod: "O" },
|
||||
Translations.t.hotkeyDocumentation.selectOsmbasedmap,
|
||||
() => setLayerCategory("osmbasedmap"),
|
||||
() => setLayerCategory("osmbasedmap")
|
||||
)
|
||||
|
||||
Hotkeys.RegisterHotkey(
|
||||
{ nomod: "M" },
|
||||
Translations.t.hotkeyDocumentation.selectMap,
|
||||
() => setLayerCategory("map"),
|
||||
() => setLayerCategory("map")
|
||||
)
|
||||
|
||||
Hotkeys.RegisterHotkey(
|
||||
{ nomod: "P" },
|
||||
Translations.t.hotkeyDocumentation.selectAerial,
|
||||
() => setLayerCategory("photo"),
|
||||
() => setLayerCategory("photo")
|
||||
)
|
||||
Hotkeys.RegisterHotkey(
|
||||
{ nomod: "L" },
|
||||
Translations.t.hotkeyDocumentation.geolocate,
|
||||
() => {
|
||||
this.geolocationControl.handleClick()
|
||||
},
|
||||
}
|
||||
)
|
||||
return true
|
||||
})
|
||||
|
@ -677,7 +684,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
|||
Translations.t.hotkeyDocumentation.translationMode,
|
||||
() => {
|
||||
Locale.showLinkToWeblate.setData(!Locale.showLinkToWeblate.data)
|
||||
},
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -688,7 +695,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
|||
const normalLayers = this.layout.layers.filter(
|
||||
(l) =>
|
||||
Constants.priviliged_layers.indexOf(<any>l.id) < 0 &&
|
||||
!l.id.startsWith("note_import"),
|
||||
!l.id.startsWith("note_import")
|
||||
)
|
||||
const maxzoom = Math.min(...normalLayers.map((l) => l.minzoom))
|
||||
|
||||
|
@ -696,7 +703,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
|||
(l) =>
|
||||
Constants.priviliged_layers.indexOf(<any>l.id) < 0 &&
|
||||
l.source.geojsonSource === undefined &&
|
||||
l.doCount,
|
||||
l.doCount
|
||||
)
|
||||
const summaryTileSource = new SummaryTileSource(
|
||||
Constants.SummaryServer,
|
||||
|
@ -705,7 +712,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
|||
this.mapProperties,
|
||||
{
|
||||
isActive: this.mapProperties.zoom.map((z) => z < maxzoom),
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
const src = new SummaryTileSourceRewriter(summaryTileSource, this.layerState.filteredLayers)
|
||||
|
@ -727,12 +734,12 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
|||
gps_location_history: this.geolocation.historicalUserLocations,
|
||||
gps_track: this.geolocation.historicalUserLocationsTrack,
|
||||
selected_element: new StaticFeatureSource(
|
||||
this.selectedElement.map((f) => (f === undefined ? empty : [f])),
|
||||
this.selectedElement.map((f) => (f === undefined ? empty : [f]))
|
||||
),
|
||||
range: new StaticFeatureSource(
|
||||
this.mapProperties.maxbounds.map((bbox) =>
|
||||
bbox === undefined ? empty : <Feature[]>[bbox.asGeoJson({ id: "range" })],
|
||||
),
|
||||
bbox === undefined ? empty : <Feature[]>[bbox.asGeoJson({ id: "range" })]
|
||||
)
|
||||
),
|
||||
current_view: this.currentView,
|
||||
favourite: this.favourites,
|
||||
|
@ -747,7 +754,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
|||
ShowDataLayer.showRange(
|
||||
this.map,
|
||||
new StaticFeatureSource([bbox.asGeoJson({ id: "range" })]),
|
||||
this.featureSwitches.featureSwitchIsTesting,
|
||||
this.featureSwitches.featureSwitchIsTesting
|
||||
)
|
||||
}
|
||||
const currentViewLayer = this.layout.layers.find((l) => l.id === "current_view")
|
||||
|
@ -761,7 +768,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
|||
currentViewLayer,
|
||||
this.layout,
|
||||
this.osmObjectDownloader,
|
||||
this.featureProperties,
|
||||
this.featureProperties
|
||||
)
|
||||
})
|
||||
}
|
||||
|
@ -805,20 +812,20 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
|||
|
||||
const lastClickLayerConfig = new LayerConfig(
|
||||
<LayerConfigJson>last_click_layerconfig,
|
||||
"last_click",
|
||||
"last_click"
|
||||
)
|
||||
const lastClickFiltered =
|
||||
lastClickLayerConfig.isShown === undefined
|
||||
? specialLayers.last_click
|
||||
: specialLayers.last_click.features.mapD((fs) =>
|
||||
fs.filter((f) => {
|
||||
const matches = lastClickLayerConfig.isShown.matchesProperties(
|
||||
f.properties,
|
||||
)
|
||||
console.debug("LastClick ", f, "matches", matches)
|
||||
return matches
|
||||
}),
|
||||
)
|
||||
fs.filter((f) => {
|
||||
const matches = lastClickLayerConfig.isShown.matchesProperties(
|
||||
f.properties
|
||||
)
|
||||
console.debug("LastClick ", f, "matches", matches)
|
||||
return matches
|
||||
})
|
||||
)
|
||||
new ShowDataLayer(this.map, {
|
||||
features: new StaticFeatureSource(lastClickFiltered),
|
||||
layer: lastClickLayerConfig,
|
||||
|
@ -863,7 +870,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
|||
this.mapProperties.rasterLayer,
|
||||
this.availableLayers,
|
||||
this.featureSwitches.backgroundLayerId,
|
||||
this.userRelatedState.preferredBackgroundLayer,
|
||||
this.userRelatedState.preferredBackgroundLayer
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
<script lang="ts">
|
||||
|
||||
import { Utils } from "../../Utils"
|
||||
import Share from "@babeard/svelte-heroicons/solid/Share"
|
||||
import { DocumentDuplicateIcon } from "@rgossiaux/svelte-heroicons/outline"
|
||||
|
@ -30,8 +29,7 @@
|
|||
}
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col w-full">
|
||||
|
||||
<div class="flex w-full flex-col">
|
||||
<div class="flex w-full">
|
||||
<div class="literal-code w-full" on:click={(e) => Utils.selectTextIn(e.target)}>
|
||||
{text}
|
||||
|
@ -48,11 +46,8 @@
|
|||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div class="flex justify-center">
|
||||
{#if isCopied}
|
||||
<Tr t={tr.copiedToClipboard} cls="thanks m-2" />
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
<script lang="ts">
|
||||
|
||||
import Translations from "../i18n/Translations"
|
||||
import { Utils } from "../../Utils"
|
||||
import Hotkeys from "../Base/Hotkeys"
|
||||
|
@ -24,7 +23,6 @@
|
|||
|
||||
let layout = state.layout
|
||||
let featureSwitches = state.featureSwitches
|
||||
|
||||
</script>
|
||||
|
||||
<div class="link-underline links-w-full m-2 flex flex-col gap-y-1">
|
||||
|
@ -51,16 +49,14 @@
|
|||
|
||||
<a
|
||||
class="flex"
|
||||
href={"https://github.com/pietervdvn/MapComplete/blob/develop/Docs/Themes/" +
|
||||
layout.id +
|
||||
".md"}
|
||||
href={"https://github.com/pietervdvn/MapComplete/blob/develop/Docs/Themes/" + layout.id + ".md"}
|
||||
target="_blank"
|
||||
>
|
||||
<DocumentChartBar class="h-6 w-6" />
|
||||
<Tr
|
||||
t={Translations.t.general.attribution.openThemeDocumentation.Subs({
|
||||
name: layout.title,
|
||||
})}
|
||||
name: layout.title,
|
||||
})}
|
||||
/>
|
||||
</a>
|
||||
|
||||
|
@ -74,10 +70,7 @@
|
|||
<Tr t={Translations.t.general.attribution.donate} />
|
||||
</a>
|
||||
|
||||
<button
|
||||
class="as-link"
|
||||
on:click={() => state.guistate.communityIndexPanelIsOpened.setData(true)}
|
||||
>
|
||||
<button class="as-link" on:click={() => state.guistate.communityIndexPanelIsOpened.setData(true)}>
|
||||
<Community class="h-6 w-6" />
|
||||
<Tr t={Translations.t.communityIndex.title} />
|
||||
</button>
|
||||
|
@ -88,10 +81,7 @@
|
|||
<MapillaryLink large={false} mapProperties={state.mapProperties} />
|
||||
</If>
|
||||
|
||||
<button
|
||||
class="as-link"
|
||||
on:click={() => state.guistate.privacyPanelIsOpened.setData(true)}
|
||||
>
|
||||
<button class="as-link" on:click={() => state.guistate.privacyPanelIsOpened.setData(true)}>
|
||||
<EyeIcon class="h-6 w-6 pr-1" />
|
||||
<Tr t={Translations.t.privacy.title} />
|
||||
</button>
|
||||
|
|
|
@ -40,9 +40,9 @@ export default class CopyrightPanel extends Combine {
|
|||
const t = Translations.t.general.attribution
|
||||
const layoutToUse = state.layout
|
||||
|
||||
const iconAttributions: BaseUIElement[] =layoutToUse.getUsedImages().map(
|
||||
CopyrightPanel.IconAttribution
|
||||
)
|
||||
const iconAttributions: BaseUIElement[] = layoutToUse
|
||||
.getUsedImages()
|
||||
.map(CopyrightPanel.IconAttribution)
|
||||
|
||||
let maintainer: BaseUIElement = undefined
|
||||
if (layoutToUse.credits !== undefined && layoutToUse.credits !== "") {
|
||||
|
|
|
@ -31,8 +31,7 @@
|
|||
*/
|
||||
let needsThemeRedirect = url.port !== "" || url.hostname.match(/^[0-9]/) || !state.layout.official
|
||||
let layoutId = state.layout.id
|
||||
let baseLink =
|
||||
`${url.protocol}//${url.host}/${needsThemeRedirect ? "theme.html" : layoutId}?`
|
||||
let baseLink = `${url.protocol}//${url.host}/${needsThemeRedirect ? "theme.html" : layoutId}?`
|
||||
|
||||
let showWelcomeMessage = true
|
||||
let enableLogin = true
|
||||
|
@ -45,7 +44,8 @@
|
|||
enableLogin: boolean,
|
||||
enableFilters: boolean,
|
||||
enableBackground: boolean,
|
||||
enableGeolocation: boolean) {
|
||||
enableGeolocation: boolean
|
||||
) {
|
||||
const layout = state.layout
|
||||
let excluded = Utils.NoNull([
|
||||
showWelcomeMessage ? undefined : "fs-welcome-message",
|
||||
|
@ -53,7 +53,6 @@
|
|||
enableFilters ? undefined : "fs-filter",
|
||||
enableBackground ? undefined : "fs-background",
|
||||
enableGeolocation ? undefined : "fs-geolocation",
|
||||
|
||||
])
|
||||
const layerParamsWhitelist: string[] = ["fs-layers-enabled=false"]
|
||||
const layerParamsBlacklist: string[] = []
|
||||
|
@ -79,9 +78,10 @@
|
|||
|
||||
const layersBlack = layerParamsBlacklist.join("&")
|
||||
const layersWhite = layerParamsWhitelist.join("&")
|
||||
const layers = layersBlack.length < layersWhite.length ? layerParamsBlacklist : layerParamsWhitelist
|
||||
const layers =
|
||||
layersBlack.length < layersWhite.length ? layerParamsBlacklist : layerParamsWhitelist
|
||||
const params = QueryParameters.GetParts(new Set(excluded))
|
||||
.filter(part => !part.startsWith("layer-"))
|
||||
.filter((part) => !part.startsWith("layer-"))
|
||||
.concat(...layers)
|
||||
.concat(excluded.map((k) => k + "=" + false))
|
||||
linkToShare = baseLink + Utils.Dedup(params).join("&")
|
||||
|
@ -91,30 +91,42 @@
|
|||
}
|
||||
}
|
||||
|
||||
$: calculateLinkToShare(showWelcomeMessage, enableLogin, enableFilters, enableBackground, enableGeolocation)
|
||||
$: calculateLinkToShare(
|
||||
showWelcomeMessage,
|
||||
enableLogin,
|
||||
enableFilters,
|
||||
enableBackground,
|
||||
enableGeolocation
|
||||
)
|
||||
|
||||
let iframeCode: string
|
||||
$: iframeCode = `<iframe src="${linkToShare}"
|
||||
${enableGeolocation ? 'allow="geolocation"' : ""} width="100%" height="100%" style="min-width: 250px; min-height: 250px"
|
||||
${
|
||||
enableGeolocation ? 'allow="geolocation"' : ""
|
||||
} width="100%" height="100%" style="min-width: 250px; min-height: 250px"
|
||||
title="${state.layout.title?.txt ?? "MapComplete"} with MapComplete">
|
||||
</iframe>`
|
||||
|
||||
Array.from(state.layerState.filteredLayers.values()).forEach(flayer => flayer.isDisplayed.addCallbackAndRunD(_ => {
|
||||
calculateLinkToShare(showWelcomeMessage, enableLogin, enableFilters, enableBackground, enableGeolocation)
|
||||
}))
|
||||
|
||||
Array.from(state.layerState.filteredLayers.values()).forEach((flayer) =>
|
||||
flayer.isDisplayed.addCallbackAndRunD((_) => {
|
||||
calculateLinkToShare(
|
||||
showWelcomeMessage,
|
||||
enableLogin,
|
||||
enableFilters,
|
||||
enableBackground,
|
||||
enableGeolocation
|
||||
)
|
||||
})
|
||||
)
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col">
|
||||
<div class="flex flex-col">
|
||||
<Tr t={tr.intro} />
|
||||
|
||||
<Copyable {state} text={linkToShare}/>
|
||||
|
||||
|
||||
<Copyable {state} text={linkToShare} />
|
||||
</div>
|
||||
<div class="flex justify-center">
|
||||
|
||||
<ToSvelte
|
||||
construct={() => new Img(new Qr(linkToShare).toImageElement(125)).SetStyle("width: 125px")}
|
||||
/>
|
||||
|
@ -122,11 +134,11 @@
|
|||
|
||||
<Tr t={tr.embedIntro} />
|
||||
|
||||
<Copyable text={iframeCode}/>
|
||||
<Copyable text={iframeCode} />
|
||||
|
||||
<AccordionSingle>
|
||||
<div slot="header">
|
||||
<Tr t={tr.options}/>
|
||||
<Tr t={tr.options} />
|
||||
</div>
|
||||
<div class="link-underline my-1 flex flex-col">
|
||||
<label>
|
||||
|
@ -139,13 +151,11 @@
|
|||
<Tr t={tr.fsUserbadge} />
|
||||
</label>
|
||||
|
||||
|
||||
<label>
|
||||
<input bind:checked={enableFilters} type="checkbox" id="share_enable_filter" />
|
||||
<Tr t={tr.fsFilter} />
|
||||
</label>
|
||||
|
||||
|
||||
<label>
|
||||
<input bind:checked={enableBackground} type="checkbox" id="share_enable_background" />
|
||||
<Tr t={tr.fsBackground} />
|
||||
|
@ -157,15 +167,16 @@
|
|||
</label>
|
||||
|
||||
<span>
|
||||
|
||||
<Tr t={tr.stateIsIncluded}/>
|
||||
<a class="inline-block w-fit cursor-pointer" on:click={() => state.guistate.filtersPanelIsOpened.set(true)}>
|
||||
<Tr t={tr.openLayers}/>
|
||||
</a>
|
||||
<Tr t={tr.stateIsIncluded} />
|
||||
<a
|
||||
class="inline-block w-fit cursor-pointer"
|
||||
on:click={() => state.guistate.filtersPanelIsOpened.set(true)}
|
||||
>
|
||||
<Tr t={tr.openLayers} />
|
||||
</a>
|
||||
</span>
|
||||
|
||||
<Tr cls="link-underline" t={tr.documentation} />
|
||||
|
||||
</div>
|
||||
</AccordionSingle>
|
||||
</div>
|
||||
|
|
|
@ -86,7 +86,7 @@
|
|||
</script>
|
||||
|
||||
{#if theme.id !== personal.id || $unlockedPersonal}
|
||||
<a class="my-1 flex w-full items-center text-ellipsis rounded low-interaction p-1" href={$href}>
|
||||
<a class="low-interaction my-1 flex w-full items-center text-ellipsis rounded p-1" href={$href}>
|
||||
<Marker icons={theme.icon} size="block h-8 w-8 sm:h-11 sm:w-11 m-1 sm:mx-2 md:mx-4 shrink-0" />
|
||||
|
||||
<span class="flex flex-col overflow-hidden text-ellipsis text-xl font-bold">
|
||||
|
|
|
@ -63,7 +63,7 @@
|
|||
|
||||
<!-- Buttons: open map, go to location, search -->
|
||||
<NextButton clss="primary w-full" on:click={() => state.guistate.themeIsOpened.setData(false)}>
|
||||
<div class="flex flex-col w-full items-center">
|
||||
<div class="flex w-full flex-col items-center">
|
||||
<div class="flex w-full justify-center text-2xl">
|
||||
<Tr t={Translations.t.general.openTheMap} />
|
||||
</div>
|
||||
|
|
|
@ -57,7 +57,6 @@
|
|||
tags.data["closed_at"] = new Date().toISOString()
|
||||
NoteCommentElement.addCommentTo(txt.data, tags, state)
|
||||
tags.ping()
|
||||
|
||||
}
|
||||
|
||||
async function reopenNote() {
|
||||
|
@ -67,7 +66,6 @@
|
|||
NoteCommentElement.addCommentTo(txt.data, tags, state)
|
||||
tags.ping()
|
||||
isProcessing.set(false)
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -89,43 +87,43 @@
|
|||
</label>
|
||||
|
||||
{#if $isProcessing}
|
||||
<Loading/>
|
||||
{:else}
|
||||
<div class="flex flex-col">
|
||||
{#if $txt?.length > 0}
|
||||
<button class="primary flex" on:click|preventDefault={() => addComment()}>
|
||||
<!-- Add a comment -->
|
||||
<Speech_bubble class="h-7 w-7 pr-2" />
|
||||
<Tr t={t.addCommentPlaceholder} />
|
||||
</button>
|
||||
{:else}
|
||||
<div class="alert w-full">
|
||||
<Tr t={t.typeText} />
|
||||
</div>
|
||||
{/if}
|
||||
<Loading />
|
||||
{:else}
|
||||
<div class="flex flex-col">
|
||||
{#if $txt?.length > 0}
|
||||
<button class="primary flex" on:click|preventDefault={() => addComment()}>
|
||||
<!-- Add a comment -->
|
||||
<Speech_bubble class="h-7 w-7 pr-2" />
|
||||
<Tr t={t.addCommentPlaceholder} />
|
||||
</button>
|
||||
{:else}
|
||||
<div class="alert w-full">
|
||||
<Tr t={t.typeText} />
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if !$isClosed}
|
||||
<button class="flex items-center" on:click|preventDefault={() => closeNote()}>
|
||||
<Resolved class="h-8 w-8 pr-2" />
|
||||
<!-- Close note -->
|
||||
{#if $txt === undefined || $txt === ""}
|
||||
<Tr t={t.closeNote} />
|
||||
{:else}
|
||||
<Tr t={t.addCommentAndClose} />
|
||||
{/if}
|
||||
</button>
|
||||
{:else}
|
||||
<button class="flex items-center" on:click|preventDefault={() => reopenNote()}>
|
||||
<!-- Reopen -->
|
||||
<Note class="h-7 w-7 pr-2" />
|
||||
{#if $txt === undefined || $txt === ""}
|
||||
<Tr t={t.reopenNote} />
|
||||
{:else}
|
||||
<Tr t={t.reopenNoteAndComment} />
|
||||
{/if}
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
{#if !$isClosed}
|
||||
<button class="flex items-center" on:click|preventDefault={() => closeNote()}>
|
||||
<Resolved class="h-8 w-8 pr-2" />
|
||||
<!-- Close note -->
|
||||
{#if $txt === undefined || $txt === ""}
|
||||
<Tr t={t.closeNote} />
|
||||
{:else}
|
||||
<Tr t={t.addCommentAndClose} />
|
||||
{/if}
|
||||
</button>
|
||||
{:else}
|
||||
<button class="flex items-center" on:click|preventDefault={() => reopenNote()}>
|
||||
<!-- Reopen -->
|
||||
<Note class="h-7 w-7 pr-2" />
|
||||
{#if $txt === undefined || $txt === ""}
|
||||
<Tr t={t.reopenNote} />
|
||||
{:else}
|
||||
<Tr t={t.reopenNoteAndComment} />
|
||||
{/if}
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</form>
|
||||
</LoginToggle>
|
||||
|
|
|
@ -66,7 +66,6 @@
|
|||
|
||||
<div class="flex">
|
||||
<div class="m-4 flex w-full flex-col">
|
||||
|
||||
<NextButton clss="primary" on:click={() => state.highlightedItem.setData({ path, schema })}>
|
||||
{#if schema.hints.question}
|
||||
{schema.hints.question}
|
||||
|
|
|
@ -104,7 +104,9 @@
|
|||
return `${singular} ${i}`
|
||||
}
|
||||
|
||||
let genIconF: (x: any) => ({ icon: string, color: string }) = <any>Function("value", "return " + schema.hints.icon)
|
||||
let genIconF: (x: any) => { icon: string; color: string } = <any>(
|
||||
Function("value", "return " + schema.hints.icon)
|
||||
)
|
||||
console.log("Icon lambda is", schema.hints.icon, path, genIconF("test"))
|
||||
|
||||
function genIcon(value: any): string {
|
||||
|
@ -117,7 +119,6 @@
|
|||
}
|
||||
return genIconF(value)?.color
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<div class="pl-2">
|
||||
|
@ -160,13 +161,14 @@
|
|||
{#if schema.hints.icon}
|
||||
<Icon clss="w-6 h-6" icon={genIcon(value)} color={genColor(value)} />
|
||||
{/if}
|
||||
{singular} {i}
|
||||
{singular}
|
||||
{i}
|
||||
|
||||
{#if schema.hints.title}
|
||||
<div class="subtle ml-2">
|
||||
<Tr t={Translations.T(genTitle(value, singular, i))}/>
|
||||
<Tr t={Translations.T(genTitle(value, singular, i))} />
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
</h3>
|
||||
<button
|
||||
class="h-fit w-fit rounded-full border border-black p-1"
|
||||
|
@ -177,9 +179,9 @@
|
|||
<TrashIcon class="h-4 w-4" />
|
||||
</button>
|
||||
</div>
|
||||
{:else if typeof value === "string"}
|
||||
{:else if typeof value === "string"}
|
||||
Builtin: <b>{value}</b>
|
||||
{:else}
|
||||
{:else}
|
||||
<Tr cls="font-bold" t={Translations.T(value?.question ?? value?.render)}/>
|
||||
{/if}
|
||||
</span>
|
||||
|
|
|
@ -128,8 +128,11 @@
|
|||
on:submit
|
||||
/>
|
||||
</div>
|
||||
<a target="_blank" href="https://github.com/pietervdvn/MapComplete/blob/develop/Docs/Tags_format.md">
|
||||
<QuestionMarkCircle class="w-6 h-6"/>
|
||||
<a
|
||||
target="_blank"
|
||||
href="https://github.com/pietervdvn/MapComplete/blob/develop/Docs/Tags_format.md"
|
||||
>
|
||||
<QuestionMarkCircle class="h-6 w-6" />
|
||||
</a>
|
||||
|
||||
{#if $feedbackKey}
|
||||
|
|
|
@ -23,7 +23,7 @@ export interface ConfigMeta {
|
|||
typesdefault?: string
|
||||
suggestions?: []
|
||||
title?: string
|
||||
multianswer?: "true" | string,
|
||||
multianswer?: "true" | string
|
||||
icon?: string
|
||||
}
|
||||
required: boolean
|
||||
|
|
|
@ -203,9 +203,7 @@
|
|||
state.selectedTab.setData(Number(tab))
|
||||
}
|
||||
|
||||
uid.AsPromise().then(
|
||||
uid => selectStateBasedOnHash(uid)
|
||||
)
|
||||
uid.AsPromise().then((uid) => selectStateBasedOnHash(uid))
|
||||
|
||||
function backToStudio() {
|
||||
console.log("Back to studio")
|
||||
|
|
|
@ -104,7 +104,7 @@
|
|||
if (id.startsWith("current_view")) {
|
||||
return currentViewLayer
|
||||
}
|
||||
if(id.startsWith("summary_")){
|
||||
if (id.startsWith("summary_")) {
|
||||
console.log("Not selecting a summary object. The summary object is", element)
|
||||
return undefined
|
||||
}
|
||||
|
@ -125,11 +125,11 @@
|
|||
state.mapProperties.installCustomKeyboardHandler(viewport)
|
||||
let canZoomIn = mapproperties.maxzoom.map(
|
||||
(mz) => mapproperties.zoom.data < mz,
|
||||
[mapproperties.zoom],
|
||||
[mapproperties.zoom]
|
||||
)
|
||||
let canZoomOut = mapproperties.minzoom.map(
|
||||
(mz) => mapproperties.zoom.data > mz,
|
||||
[mapproperties.zoom],
|
||||
[mapproperties.zoom]
|
||||
)
|
||||
|
||||
function updateViewport() {
|
||||
|
@ -166,7 +166,7 @@
|
|||
onDestroy(
|
||||
rasterLayer.addCallbackAndRunD((l) => {
|
||||
rasterLayerName = l.properties.name
|
||||
}),
|
||||
})
|
||||
)
|
||||
let previewedImage = state.previewedImage
|
||||
|
||||
|
@ -197,7 +197,7 @@
|
|||
let openMapButton: UIEventSource<HTMLElement> = new UIEventSource<HTMLElement>(undefined)
|
||||
let openMenuButton: UIEventSource<HTMLElement> = new UIEventSource<HTMLElement>(undefined)
|
||||
let openCurrentViewLayerButton: UIEventSource<HTMLElement> = new UIEventSource<HTMLElement>(
|
||||
undefined,
|
||||
undefined
|
||||
)
|
||||
let _openNewElementButton: HTMLButtonElement
|
||||
let openNewElementButton: UIEventSource<HTMLElement> = new UIEventSource<HTMLElement>(undefined)
|
||||
|
@ -257,7 +257,9 @@
|
|||
on:keydown={forwardEventToMap}
|
||||
htmlElem={openMapButton}
|
||||
>
|
||||
<div class="m-0.5 mx-1 flex cursor-pointer items-center max-[480px]:w-full sm:mx-1 md:mx-2">
|
||||
<div
|
||||
class="m-0.5 mx-1 flex cursor-pointer items-center max-[480px]:w-full sm:mx-1 md:mx-2"
|
||||
>
|
||||
<Marker icons={layout.icon} size="h-4 w-4 md:h-8 md:w-8 mr-0.5 sm:mr-1 md:mr-2" />
|
||||
<b class="mr-1">
|
||||
<Tr t={layout.title} />
|
||||
|
@ -354,10 +356,10 @@
|
|||
<a
|
||||
class="bg-black-transparent pointer-events-auto ml-1 h-fit max-h-12 cursor-pointer self-end self-center overflow-hidden rounded-2xl px-1 text-white opacity-50 hover:opacity-100"
|
||||
on:click={() => {
|
||||
if(featureSwitches.featureSwitchWelcomeMessage.data){
|
||||
state.guistate.themeViewTab.setData("copyright")
|
||||
state.guistate.themeIsOpened.setData(true)
|
||||
}else{
|
||||
if (featureSwitches.featureSwitchWelcomeMessage.data) {
|
||||
state.guistate.themeViewTab.setData("copyright")
|
||||
state.guistate.themeIsOpened.setData(true)
|
||||
} else {
|
||||
state.guistate.copyrightPanelIsOpened.setData(true)
|
||||
}
|
||||
}}
|
||||
|
@ -520,11 +522,10 @@
|
|||
<Tr t={Translations.t.general.attribution.title} />
|
||||
</div>
|
||||
|
||||
<div slot="content2" class="flex flex-col m-2">
|
||||
<div slot="content2" class="m-2 flex flex-col">
|
||||
<ToSvelte construct={() => new CopyrightPanel(state)} />
|
||||
</div>
|
||||
|
||||
|
||||
<div class="flex" slot="title3">
|
||||
<Share class="h-4 w-4" />
|
||||
<Tr t={Translations.t.general.sharescreen.title} />
|
||||
|
@ -650,15 +651,14 @@
|
|||
<FloatOver on:close={() => state.guistate.privacyPanelIsOpened.setData(false)}>
|
||||
<div class="flex h-full flex-col overflow-hidden">
|
||||
<h1 class="low-interaction m-0 flex items-center p-4 drop-shadow-md">
|
||||
<Tr t= {Translations.t.general.attribution.title}/>
|
||||
<Tr t={Translations.t.general.attribution.title} />
|
||||
</h1>
|
||||
<div class="overflow-auto p-4">
|
||||
<h2>
|
||||
|
||||
<Tr t={Translations.t.general.menu.aboutMapComplete} />
|
||||
<Tr t={Translations.t.general.menu.aboutMapComplete} />
|
||||
</h2>
|
||||
<AboutMapComplete {state} />
|
||||
<ToSvelte construct={() => new CopyrightPanel(state)} />
|
||||
<AboutMapComplete {state} />
|
||||
<ToSvelte construct={() => new CopyrightPanel(state)} />
|
||||
</div>
|
||||
</div>
|
||||
</FloatOver>
|
||||
|
|
13
src/Utils.ts
13
src/Utils.ts
|
@ -978,7 +978,16 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
|||
}
|
||||
}
|
||||
xhr.send(content)
|
||||
xhr.onerror = (ev: ProgressEvent<EventTarget>) => reject("Could not get "+url+", xhr status code is "+xhr.status+" ("+xhr.statusText+")")
|
||||
xhr.onerror = (ev: ProgressEvent<EventTarget>) =>
|
||||
reject(
|
||||
"Could not get " +
|
||||
url +
|
||||
", xhr status code is " +
|
||||
xhr.status +
|
||||
" (" +
|
||||
xhr.statusText +
|
||||
")"
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1072,7 +1081,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
|||
const injected = Utils.injectedDownloads[url]
|
||||
if (injected !== undefined) {
|
||||
console.debug("Using injected resource for test for URL", url)
|
||||
return {content: injected}
|
||||
return { content: injected }
|
||||
}
|
||||
const result = await Utils.downloadAdvanced(
|
||||
url,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"contributors": [
|
||||
{
|
||||
"commits": 7612,
|
||||
"commits": 7646,
|
||||
"contributor": "Pieter Vander Vennet"
|
||||
},
|
||||
{
|
||||
|
|
|
@ -469,9 +469,9 @@
|
|||
"na"
|
||||
],
|
||||
"NZ": [
|
||||
"en",
|
||||
"en",
|
||||
"mi",
|
||||
"en",
|
||||
"mi"
|
||||
],
|
||||
"OM": [
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
{
|
||||
"ca": "català",
|
||||
"cs": "čeština",
|
||||
"da": "dansk",
|
||||
"de": "Deutsch",
|
||||
"en": "English",
|
||||
|
@ -12,7 +11,7 @@
|
|||
"gl": "lingua galega",
|
||||
"he": "עברית",
|
||||
"hu": "magyar",
|
||||
"id": "bahasa Indonesia",
|
||||
"id": "Indonesia",
|
||||
"it": "italiano",
|
||||
"ja": "日本語",
|
||||
"nb_NO": "bokmål",
|
||||
|
@ -23,6 +22,7 @@
|
|||
"ru": "русский язык",
|
||||
"sl": "slovenščina",
|
||||
"sv": "svenska",
|
||||
"zgh": "ⵜⴰⵎⴰⵣⵉⵖⵜ ⵜⴰⵏⴰⵡⴰⵢⵜ ⵜⴰⵎⵖⵔⵉⴱⵉⵜ",
|
||||
"zh_Hans": "简体中文",
|
||||
"zh_Hant": "繁體中文"
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -1,11 +1,11 @@
|
|||
{
|
||||
"contributors": [
|
||||
{
|
||||
"commits": 378,
|
||||
"commits": 382,
|
||||
"contributor": "Pieter Vander Vennet"
|
||||
},
|
||||
{
|
||||
"commits": 351,
|
||||
"commits": 352,
|
||||
"contributor": "kjon"
|
||||
},
|
||||
{
|
||||
|
@ -21,7 +21,7 @@
|
|||
"contributor": "Robin van der Linde"
|
||||
},
|
||||
{
|
||||
"commits": 69,
|
||||
"commits": 70,
|
||||
"contributor": "mcliquid"
|
||||
},
|
||||
{
|
||||
|
@ -84,6 +84,10 @@
|
|||
"commits": 18,
|
||||
"contributor": "el_libre como el chaval"
|
||||
},
|
||||
{
|
||||
"commits": 15,
|
||||
"contributor": "Patchanka64"
|
||||
},
|
||||
{
|
||||
"commits": 15,
|
||||
"contributor": "macpac"
|
||||
|
@ -112,10 +116,6 @@
|
|||
"commits": 13,
|
||||
"contributor": "Joost"
|
||||
},
|
||||
{
|
||||
"commits": 12,
|
||||
"contributor": "Patchanka64"
|
||||
},
|
||||
{
|
||||
"commits": 12,
|
||||
"contributor": "Ettore Atalan"
|
||||
|
@ -132,6 +132,10 @@
|
|||
"commits": 11,
|
||||
"contributor": "Túllio Franca"
|
||||
},
|
||||
{
|
||||
"commits": 10,
|
||||
"contributor": "Manuel Tassi"
|
||||
},
|
||||
{
|
||||
"commits": 10,
|
||||
"contributor": "brunnerpaul"
|
||||
|
@ -158,7 +162,7 @@
|
|||
},
|
||||
{
|
||||
"commits": 9,
|
||||
"contributor": "Manuel Tassi"
|
||||
"contributor": "hugoalh"
|
||||
},
|
||||
{
|
||||
"commits": 9,
|
||||
|
@ -212,10 +216,6 @@
|
|||
"commits": 7,
|
||||
"contributor": "Niels Elgaard Larsen"
|
||||
},
|
||||
{
|
||||
"commits": 6,
|
||||
"contributor": "hugoalh"
|
||||
},
|
||||
{
|
||||
"commits": 6,
|
||||
"contributor": "Juele juele"
|
||||
|
@ -256,6 +256,10 @@
|
|||
"commits": 6,
|
||||
"contributor": "lvgx"
|
||||
},
|
||||
{
|
||||
"commits": 5,
|
||||
"contributor": "Franco"
|
||||
},
|
||||
{
|
||||
"commits": 5,
|
||||
"contributor": "Christian Schmidt"
|
||||
|
@ -328,10 +332,6 @@
|
|||
"commits": 3,
|
||||
"contributor": "Paolo Mauri"
|
||||
},
|
||||
{
|
||||
"commits": 3,
|
||||
"contributor": "Franco"
|
||||
},
|
||||
{
|
||||
"commits": 3,
|
||||
"contributor": "Peter Brodersen"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue