Fixes, surveillance cams v0.1

This commit is contained in:
Pieter Vander Vennet 2020-11-13 23:58:11 +01:00
parent b329bbbdb3
commit ba44024dd9
10 changed files with 137 additions and 65 deletions

View file

@ -15,10 +15,11 @@ import * as benches from "../assets/themes/benches/benches.json";
import * as charging_stations from "../assets/themes/charging_stations/charging_stations.json" import * as charging_stations from "../assets/themes/charging_stations/charging_stations.json"
import * as widths from "../assets/themes/widths/width.json" import * as widths from "../assets/themes/widths/width.json"
import * as drinking_water from "../assets/themes/drinking_water/drinking_water.json" import * as drinking_water from "../assets/themes/drinking_water/drinking_water.json"
import LayerConfig from "./JSON/LayerConfig"; import * as surveillance_cameras from "../assets/themes/surveillance_cameras/surveillance_cameras.json"
import SharedLayers from "./SharedLayers";
import * as personal from "../assets/themes/personalLayout/personalLayout.json" import * as personal from "../assets/themes/personalLayout/personalLayout.json"
import LayerConfig from "./JSON/LayerConfig";
import LayoutConfig from "./JSON/LayoutConfig"; import LayoutConfig from "./JSON/LayoutConfig";
import SharedLayers from "./SharedLayers";
export class AllKnownLayouts { export class AllKnownLayouts {
@ -60,6 +61,7 @@ export class AllKnownLayouts {
new LayoutConfig(widths), new LayoutConfig(widths),
new LayoutConfig(buurtnatuur), new LayoutConfig(buurtnatuur),
new LayoutConfig(bike_monitoring_stations), new LayoutConfig(bike_monitoring_stations),
new LayoutConfig(surveillance_cameras)
]; ];

View file

@ -442,7 +442,7 @@ export class InitUiElements {
State.state.layerUpdater = new UpdateFromOverpass(State.state); State.state.layerUpdater = new UpdateFromOverpass(State.state);
State.state.availableBackgroundLayers = new AvailableBaseLayers(State.state).availableEditorLayers; State.state.availableBackgroundLayers = new AvailableBaseLayers(State.state).availableEditorLayers;
const queryParam = QueryParameters.GetQueryParameter("background", State.state.layoutToUse.data.defaultBackgroundId); const queryParam = QueryParameters.GetQueryParameter("background", State.state.layoutToUse.data.defaultBackgroundId, "The id of the background layer to start with");
queryParam.addCallbackAndRun((selectedId: string) => { queryParam.addCallbackAndRun((selectedId: string) => {
const available = State.state.availableBackgroundLayers.data; const available = State.state.availableBackgroundLayers.data;
@ -483,7 +483,7 @@ export class InitUiElements {
const flayer: FilteredLayer = FilteredLayer.fromDefinition(layer, generateInfo); const flayer: FilteredLayer = FilteredLayer.fromDefinition(layer, generateInfo);
flayers.push(flayer); flayers.push(flayer);
QueryParameters.GetQueryParameter("layer-" + layer.id, "true") QueryParameters.GetQueryParameter("layer-" + layer.id, "true", "Wehter or not layer "+layer.id+" is shown")
.map<boolean>((str) => str !== "false", [], (b) => b.toString()) .map<boolean>((str) => str !== "false", [], (b) => b.toString())
.syncWith( .syncWith(
flayer.isDisplayed flayer.isDisplayed

View file

@ -5,20 +5,22 @@ import {UIEventSource} from "../UIEventSource";
export class QueryParameters { export class QueryParameters {
private static order: string [] = ["layout","test","z","lat","lon"]; private static order: string [] = ["layout", "test", "z", "lat", "lon"];
private static knownSources = {}; private static knownSources = {};
private static initialized = false; private static initialized = false;
private static defaults = {} private static defaults = {}
private static addOrder(key){ private static documentation = {}
if(this.order.indexOf(key) < 0){
private static addOrder(key) {
if (this.order.indexOf(key) < 0) {
this.order.push(key) this.order.push(key)
} }
} }
private static init() { private static init() {
if(this.initialized){ if (this.initialized) {
return; return;
} }
this.initialized = true; this.initialized = true;
@ -63,6 +65,7 @@ export class QueryParameters {
if(!this.initialized){ if(!this.initialized){
this.init(); this.init();
} }
QueryParameters.documentation[key] = documentation;
if (deflt !== undefined) { if (deflt !== undefined) {
QueryParameters.defaults[key] = deflt; QueryParameters.defaults[key] = deflt;
} }
@ -76,4 +79,12 @@ export class QueryParameters {
return source; return source;
} }
public static GenerateQueryParameterDocs(): string {
const docs = [];
for (const key in QueryParameters.documentation) {
docs.push("**" + key + "**: " + QueryParameters.documentation[key] + " (default value: _" + QueryParameters.defaults[key] + "_)")
}
return docs.join("\n\n");
}
} }

View file

@ -103,7 +103,7 @@ A theme has translations into the preset.json (`assets/themes/themename/themenam
1. Modify `"language"` to contain the new language, e.g. `"language": "nl"` becomes `"language": ["nl", "en"]` 1. Modify `"language"` to contain the new language, e.g. `"language": "nl"` becomes `"language": ["nl", "en"]`
2. Add extra strings to the texts. If it used to be a single-language theme, one can replace the strings, e.g.: `"description": "Welcome to Open Bookcase Map"` to `"description": {"en": "Welcome to Open Bookcase Map", "nl": "Welkom bij de OpenBoekenruilkastenKaart", "fr": "Bienvenue sûr la carte des petites bibliotheques"}`. If the correct language is not found, it'll fallback to another supported language. 2. Add extra strings to the texts. If it used to be a single-language theme, one can replace the strings, e.g.: `"description": "Welcome to Open Bookcase Map"` to `"description": {"en": "Welcome to Open Bookcase Map", "nl": "Welkom bij de OpenBoekenruilkastenKaart", "fr": "Bienvenue sûr la carte des petites bibliotheques"}`. If the correct language is not found, it'll fallback to another supported language.
3. If you notice missing translations in the core of MapComplete, fork this project, open [the file containing all translations](https://github.com/pietervdvn/MapComplete/blob/master/UI/i18n/Translations.ts), add add a language string there 3. If you notice missing translations in the core of MapComplete, fork this project, open [the file containing all translations](https://github.com/pietervdvn/MapComplete/blob/master/assets/translations.json), add add a language string there
4. Send a pull request to update the languages, I'll gladly add it! It doesn't have to be a complete translation from the start ;) 4. Send a pull request to update the languages, I'll gladly add it! It doesn't have to be a complete translation from the start ;)
### Adding your theme to the repository ### Adding your theme to the repository
@ -165,6 +165,50 @@ Whenever a change is made -even adding a single tag- the change is uploaded into
Note that changesets are closed automatically after one hour of inactivity, so we don't have to worry about closing them. Note that changesets are closed automatically after one hour of inactivity, so we don't have to worry about closing them.
### Query parameters
By adding extra query parameters, more options are available to influence:
**test**: 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 (default value: _false_)
**layout**: The layout to load into MapComplete (default value: _bookcases_)
**userlayout**: undefined (default value: _false_)
**layer-control-toggle**: Wether or not the layer control is shown (default value: _false_)
**tab**: The tab that is shown in the welcome-message. 0 = the explanation of the theme,1 = OSM-credits, 2 = sharescreen, 3 = more themes, 4 = about mapcomplete (user must be logged in and have >200 changesets) (default value: _0_)
**z**: The initial/current zoom level (default value: _1_)
**lat**: The initial/current latitude (default value: _0_)
**lon**: The initial/current longitude of the app (default value: _0_)
**fs-userbadge**: Disables/Enables the userbadge (and thus disables login capabilities) (default value: _true_)
**fs-search**: Disables/Enables the search bar (default value: _true_)
**fs-layers**: Disables/Enables the layer control (default value: _true_)
**fs-add-new**: Disables/Enables the 'add new feature'-popup. (A theme without presets might not have it in the first place) (default value: _true_)
**fs-welcome-message**: undefined (default value: _true_)
**fs-iframe**: Disables/Enables the iframe-popup (default value: _false_)
**fs-more-quests**: Disables/Enables the 'More Quests'-tab in the welcome message (default value: _true_)
**fs-share-screen**: Disables/Enables the 'Share-screen'-tab in the welcome message (default value: _true_)
**fs-geolocation**: Disables/Enables the geolocation button (default value: _true_)
**oauth_token**: Used to complete the login (default value: _undefined_)
**background**: The id of the background layer to start with (default value: _undefined_)
**layer-bookcases**: Wehter or not layer bookcases is shown (default value: _true_) index.ts:104:8
# Privacy # Privacy
Privacy is important, we try to leak as little information as possible. Privacy is important, we try to leak as little information as possible.

View file

@ -115,10 +115,10 @@ export default class State {
public layoutDefinition: string; public layoutDefinition: string;
public installedThemes: UIEventSource<{ layout: LayoutConfig; definition: string }[]>; public installedThemes: UIEventSource<{ layout: LayoutConfig; definition: string }[]>;
public layerControlIsOpened: UIEventSource<boolean> = QueryParameters.GetQueryParameter("layer-control-toggle", "false") public layerControlIsOpened: UIEventSource<boolean> = QueryParameters.GetQueryParameter("layer-control-toggle", "false", "Wether or not the layer control is shown")
.map<boolean>((str) => str !== "false", [], b => "" + b) .map<boolean>((str) => str !== "false", [], b => "" + b)
public welcomeMessageOpenedTab = QueryParameters.GetQueryParameter("tab", "0").map<number>( public welcomeMessageOpenedTab = QueryParameters.GetQueryParameter("tab", "0", `The tab that is shown in the welcome-message. 0 = the explanation of the theme,1 = OSM-credits, 2 = sharescreen, 3 = more themes, 4 = about mapcomplete (user must be logged in and have >${State.userJourney.mapCompleteHelpUnlock} changesets)`).map<number>(
str => isNaN(Number(str)) ? 0 : Number(str), [], n => "" + n str => isNaN(Number(str)) ? 0 : Number(str), [], n => "" + n
); );
@ -138,11 +138,11 @@ export default class State {
}) })
} }
this.zoom = asFloat( this.zoom = asFloat(
QueryParameters.GetQueryParameter("z", "" + layoutToUse.startZoom) QueryParameters.GetQueryParameter("z", "" + layoutToUse.startZoom, "The initial/current zoom level")
.syncWith(LocalStorageSource.Get("zoom"))); .syncWith(LocalStorageSource.Get("zoom")));
this.lat = asFloat(QueryParameters.GetQueryParameter("lat", "" + layoutToUse.startLat) this.lat = asFloat(QueryParameters.GetQueryParameter("lat", "" + layoutToUse.startLat, "The initial/current latitude")
.syncWith(LocalStorageSource.Get("lat"))); .syncWith(LocalStorageSource.Get("lat")));
this.lon = asFloat(QueryParameters.GetQueryParameter("lon", "" + layoutToUse.startLon) this.lon = asFloat(QueryParameters.GetQueryParameter("lon", "" + layoutToUse.startLon, "The initial/current longitude of the app")
.syncWith(LocalStorageSource.Get("lon"))); .syncWith(LocalStorageSource.Get("lon")));
@ -165,8 +165,8 @@ export default class State {
}); });
function featSw(key: string, deflt: (layout: LayoutConfig) => boolean, documentation?: string): UIEventSource<boolean> { function featSw(key: string, deflt: (layout: LayoutConfig) => boolean, documentation: string): UIEventSource<boolean> {
const queryParameterSource = QueryParameters.GetQueryParameter(key, undefined); const queryParameterSource = QueryParameters.GetQueryParameter(key, undefined, documentation);
// I'm so sorry about someone trying to decipher this // I'm so sorry about someone trying to decipher this
// It takes the current layout, extracts the default value for this query paramter. A query parameter event source is then retreived and flattened // It takes the current layout, extracts the default value for this query paramter. A query parameter event source is then retreived and flattened
@ -180,20 +180,30 @@ export default class State {
this.featureSwitchUserbadge = featSw("fs-userbadge", (layoutToUse) => layoutToUse?.enableUserBadge ?? true, this.featureSwitchUserbadge = featSw("fs-userbadge", (layoutToUse) => layoutToUse?.enableUserBadge ?? true,
"Disables the userbadge (and thus disables login capabilities)"); "Disables/Enables the user information pill (userbadge) at the top left. Disabling this disables logging in and thus disables editing all together, effectively putting MapComplete into read-only mode.");
this.featureSwitchSearch = featSw("fs-search", (layoutToUse) => layoutToUse?.enableSearch ?? true); this.featureSwitchSearch = featSw("fs-search", (layoutToUse) => layoutToUse?.enableSearch ?? true,
this.featureSwitchLayers = featSw("fs-layers", (layoutToUse) => layoutToUse?.enableLayers ?? true); "Disables/Enables the search bar");
this.featureSwitchAddNew = featSw("fs-add-new", (layoutToUse) => layoutToUse?.enableAddNewPoints ?? true); this.featureSwitchLayers = featSw("fs-layers", (layoutToUse) => layoutToUse?.enableLayers ?? true,
this.featureSwitchWelcomeMessage = featSw("fs-welcome-message", () => true); "Disables/Enables the layer control");
this.featureSwitchIframe = featSw("fs-iframe", () => false); this.featureSwitchAddNew = featSw("fs-add-new", (layoutToUse) => layoutToUse?.enableAddNewPoints ?? true,
this.featureSwitchMoreQuests = featSw("fs-more-quests", (layoutToUse) => layoutToUse?.enableMoreQuests ?? true); "Disables/Enables the 'add new feature'-popup. (A theme without presets might not have it in the first place)");
this.featureSwitchShareScreen = featSw("fs-share-screen", (layoutToUse) => layoutToUse?.enableShareScreen ?? true); this.featureSwitchWelcomeMessage = featSw("fs-welcome-message", () => true,
this.featureSwitchGeolocation = featSw("fs-geolocation", (layoutToUse) => layoutToUse?.enableGeolocation ?? true); "Disables/enables the help menu or welcome message");
this.featureSwitchIframe = featSw("fs-iframe", () => false,
"Disables/Enables the iframe-popup");
this.featureSwitchMoreQuests = featSw("fs-more-quests", (layoutToUse) => layoutToUse?.enableMoreQuests ?? true,
"Disables/Enables the 'More Quests'-tab in the welcome message");
this.featureSwitchShareScreen = featSw("fs-share-screen", (layoutToUse) => layoutToUse?.enableShareScreen ?? true,
"Disables/Enables the 'Share-screen'-tab in the welcome message");
this.featureSwitchGeolocation = featSw("fs-geolocation", (layoutToUse) => layoutToUse?.enableGeolocation ?? true,
"Disables/Enables the geolocation button");
const testParam = QueryParameters.GetQueryParameter("test", "false").data; const testParam = QueryParameters.GetQueryParameter("test", "false",
"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").data;
this.osmConnection = new OsmConnection( this.osmConnection = new OsmConnection(
testParam === "true", testParam === "true",
QueryParameters.GetQueryParameter("oauth_token", undefined), QueryParameters.GetQueryParameter("oauth_token", undefined,
"Used to complete the login"),
layoutToUse.id, layoutToUse.id,
true true
); );

View file

@ -42,6 +42,9 @@ export default class DeleteImage extends UIElement {
} }
InnerRender(): string { InnerRender(): string {
if(!State.state.featureSwitchUserbadge.data){
return "";
}
const value = this.tags.data[this.key]; const value = this.tags.data[this.key];
if (value === undefined || value === "") { if (value === undefined || value === "") {

View file

@ -51,6 +51,10 @@ export class ImageUploadFlow extends UIElement {
} }
InnerRender(): string { InnerRender(): string {
if(!State.state.featureSwitchUserbadge.data){
return "";
}
const t = Translations.t.image; const t = Translations.t.image;
if (State.state.osmConnection.userDetails === undefined) { if (State.state.osmConnection.userDetails === undefined) {

View file

@ -9,25 +9,11 @@ import {SubstitutedTranslation} from "../SpecialVisualizations";
export default class TagRenderingAnswer extends UIElement { export default class TagRenderingAnswer extends UIElement {
private _tags: UIEventSource<any>; private _tags: UIEventSource<any>;
private _configuration: TagRenderingConfig; private _configuration: TagRenderingConfig;
private _content: UIElement;
constructor(tags: UIEventSource<any>, configuration: TagRenderingConfig) { constructor(tags: UIEventSource<any>, configuration: TagRenderingConfig) {
super(tags); super(tags);
this._tags = tags; this._tags = tags;
this._configuration = configuration; this._configuration = configuration;
const self = this;
tags.addCallbackAndRun(tags => {
if (tags === undefined) {
self._content = undefined
return;
}
const tr = this._configuration.GetRenderValue(tags);
if (tr === undefined) {
self._content = undefined
return
}
self._content = new SubstitutedTranslation(tr, self._tags)
})
} }
InnerRender(): string { InnerRender(): string {
@ -36,10 +22,16 @@ export default class TagRenderingAnswer extends UIElement {
return ""; return "";
} }
} }
if(this._content === undefined){
const tags = this._tags.data;
if (tags === undefined) {
return ""; return "";
} }
return this._content.Render(); const tr = this._configuration.GetRenderValue(tags);
if (tr === undefined) {
return "";
}
return new SubstitutedTranslation(tr, this._tags).Render();
} }
} }

View file

@ -156,8 +156,8 @@
"key": "surveillance:type" "key": "surveillance:type"
}, },
"render": { "render": {
"en": " Surveills a {surveillance:type}", "en": " Surveills a {surveillance:zone}",
"nl": "Bewaakt een {surveillance:type}" "nl": "Bewaakt een {surveillance:zone}"
}, },
"mappings": [ "mappings": [
{ {
@ -193,6 +193,28 @@
"nl": "Bewaakt een ingang" "nl": "Bewaakt een ingang"
} }
}, },
{
"if": {
"and": [
"surveillance:zone=corridor"
]
},
"then": {
"en": "Surveills a corridor",
"nl": "Bewaakt een gang"
}
},
{
"if": {
"and": [
"surveillance:zone=public_transport_platform"
]
},
"then": {
"en": "Surveills a public tranport platform",
"nl": "Bewaakt een perron of bushalte"
}
},
{ {
"if": { "if": {
"and": [ "and": [

View file

@ -4,7 +4,6 @@ import {InitUiElements} from "./InitUiElements";
import {QueryParameters} from "./Logic/Web/QueryParameters"; import {QueryParameters} from "./Logic/Web/QueryParameters";
import {UIEventSource} from "./Logic/UIEventSource"; import {UIEventSource} from "./Logic/UIEventSource";
import * as $ from "jquery"; import * as $ from "jquery";
import SharedLayers from "./Customizations/SharedLayers";
import LayoutConfig from "./Customizations/JSON/LayoutConfig"; import LayoutConfig from "./Customizations/JSON/LayoutConfig";
let defaultLayout = "bookcases" let defaultLayout = "bookcases"
@ -54,23 +53,7 @@ if (path !== "index.html" && path !== "") {
defaultLayout = path.substr(0, path.length - 5); defaultLayout = path.substr(0, path.length - 5);
console.log("Using layout", defaultLayout); console.log("Using layout", defaultLayout);
} }
defaultLayout = QueryParameters.GetQueryParameter("layout", defaultLayout,"The layout to load into MapComplete").data;
// Run over all questsets. If a part of the URL matches a searched-for part in the layout, it'll take that as the default
for (const k in AllKnownLayouts.allSets) {
const layout : LayoutConfig= AllKnownLayouts.allSets[k];
const possibleParts = (layout.locationContains ?? []);
for (const locationMatch of possibleParts) {
if (locationMatch === "") {
continue
}
if (window.location.href.toLowerCase().indexOf(locationMatch.toLowerCase()) >= 0) {
defaultLayout = layout.name;
}
}
}
defaultLayout = QueryParameters.GetQueryParameter("layout", defaultLayout).data;
let layoutToUse: LayoutConfig = AllKnownLayouts.allSets[defaultLayout.toLowerCase()] ?? AllKnownLayouts["all"]; let layoutToUse: LayoutConfig = AllKnownLayouts.allSets[defaultLayout.toLowerCase()] ?? AllKnownLayouts["all"];
@ -118,3 +101,4 @@ if (layoutFromBase64.startsWith("wiki:")) {
InitUiElements.InitAll(layoutToUse, layoutFromBase64, testing, defaultLayout); InitUiElements.InitAll(layoutToUse, layoutFromBase64, testing, defaultLayout);
} }
// console.log(QueryParameters.GenerateQueryParameterDocs())