Merge develop

This commit is contained in:
pietervdvn 2021-07-16 02:25:39 +02:00
commit 42391b4ff1
20 changed files with 362 additions and 199 deletions

View file

@ -42,6 +42,7 @@ export default class LayoutConfig {
public readonly enableGeolocation: boolean;
public readonly enableBackgroundLayerSelection: boolean;
public readonly enableShowAllQuestions: boolean;
public readonly enableExportButton: boolean;
public readonly customCss?: string;
/*
How long is the cache valid, in seconds?
@ -152,6 +153,7 @@ export default class LayoutConfig {
this.enableAddNewPoints = json.enableAddNewPoints ?? true;
this.enableBackgroundLayerSelection = json.enableBackgroundLayerSelection ?? true;
this.enableShowAllQuestions = json.enableShowAllQuestions ?? false;
this.enableExportButton = json.enableExportButton ?? false;
this.customCss = json.customCss;
this.cacheTimeout = json.cacheTimout ?? (60 * 24 * 60 * 60)

View file

@ -15,6 +15,7 @@ import UnitConfigJson from "./UnitConfigJson";
* General remark: a type (string | any) indicates either a fixed or a translatable string.
*/
export interface LayoutConfigJson {
/**
* The id of this layout.
*
@ -335,4 +336,5 @@ export interface LayoutConfigJson {
enableGeolocation?: boolean;
enableBackgroundLayerSelection?: boolean;
enableShowAllQuestions?: boolean;
enableExportButton?: boolean;
}

View file

@ -20,126 +20,158 @@ the URL-parameters are stated in the part between the `?` and the `#`. There are
Finally, the URL-hash is the part after the `#`. It is `node/1234` in this case.
layer-control-toggle
----------------------
Whether or not the layer control is shown The default value is _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 >50 changesets) The default value is _0_
z
---
The initial/current zoom level The default value is _0_
lat
-----
The initial/current latitude The default value is _0_
lon
-----
The initial/current longitude of the app The default value is _0_
fs-userbadge
--------------
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. The default value is _true_
fs-search
-----------
Disables/Enables the search bar The default value is _true_
fs-layers
-----------
Disables/Enables the layer control The default value is _true_
fs-add-new
------------
Disables/Enables the 'add new feature'-popup. (A theme without presets might not have it in the first place) The default value is _true_
fs-welcome-message
--------------------
Disables/enables the help menu or welcome message The default value is _true_
fs-iframe
-----------
Disables/Enables the iframe-popup The default value is _false_
fs-more-quests
----------------
Disables/Enables the 'More Quests'-tab in the welcome message The default value is _true_
fs-share-screen
-----------------
Disables/Enables the 'Share-screen'-tab in the welcome message The default value is _true_
fs-geolocation
----------------
Disables/Enables the geolocation button The default value is _true_
fs-all-questions
------------------
Always show all questions The default value is _false_
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 The default value is _false_
debug
-------
If true, shows some extra debugging help such as all the available tags on every object The default value is _false_
backend
backend
---------
The OSM backend to use - can be used to redirect mapcomplete to the testing backend when using 'osm-test' The default value is _osm_
The OSM backend to use - can be used to redirect mapcomplete to the testing backend when using 'osm-test' The default value is _osm_
custom-css
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 The default value is _false_
layout
--------
The layout to load into MapComplete The default value is __
userlayout
------------
If specified, the custom css from the given link will be loaded additionaly The default value is __
If not 'false', a custom (non-official) theme is loaded. This custom layout can be done in multiple ways:
- The hash of the URL contains a base64-encoded .json-file containing the theme definition
- The hash of the URL contains a lz-compressed .json-file, as generated by the custom theme generator
- The parameter itself is an URL, in which case that URL will be downloaded. It should point to a .json of a theme The default value is _false_
background
layer-control-toggle
----------------------
Whether or not the layer control is shown The default value is _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 >50 changesets) The default value is _0_
z
---
The initial/current zoom level The default value is _14_
lat
-----
The initial/current latitude The default value is _51.2095_
lon
-----
The initial/current longitude of the app The default value is _3.2228_
fs-userbadge
--------------
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. The default value is _true_
fs-search
-----------
Disables/Enables the search bar The default value is _true_
fs-layers
-----------
Disables/Enables the layer control The default value is _true_
fs-add-new
------------
The id of the background layer to start with The default value is _osm_
Disables/Enables the 'add new feature'-popup. (A theme without presets might not have it in the first place) The default value is _true_
fs-welcome-message
--------------------
Disables/enables the help menu or welcome message The default value is _true_
fs-iframe
-----------
Disables/Enables the iframe-popup The default value is _false_
fs-more-quests
----------------
Disables/Enables the 'More Quests'-tab in the welcome message The default value is _true_
fs-share-screen
-----------------
Disables/Enables the 'Share-screen'-tab in the welcome message The default value is _true_
fs-geolocation
----------------
Disables/Enables the geolocation button The default value is _true_
fs-all-questions
------------------
Always show all questions The default value is _false_
fs-export
-----------
If set, enables the 'download'-button to download everything as geojson The default value is _false_
fake-user
-----------
If true, 'dryrun' mode is activated and a fake user account is loaded The default value is _false_
debug
-------
If true, shows some extra debugging help such as all the available tags on every object The default value is _false_
custom-css
------------
If specified, the custom css from the given link will be loaded additionaly The default value is __
background
------------
The id of the background layer to start with The default value is _osm_
oauth_token
-------------
Used to complete the login No default value set
layer-<layer-id>
------------------

View file

@ -427,6 +427,8 @@ export class InitUiElements {
state.locationControl,
state.selectedElement);
State.state.featurePipeline = source;
new ShowDataLayer(source.features, State.state.leafletMap, State.state.layoutToUse);
const selectedFeatureHandler = new SelectedFeatureHandler(Hash.hash, State.state.selectedElement, source, State.state.osmApiFeatureSource);

View file

@ -1,9 +1,45 @@
import {UIEventSource} from "../UIEventSource";
import {Utils} from "../../Utils";
export default interface FeatureSource {
features: UIEventSource<{feature: any, freshness: Date}[]>;
features: UIEventSource<{ feature: any, freshness: Date }[]>;
/**
* Mainly used for debuging
*/
name: string;
}
export class FeatureSourceUtils {
/**
* Exports given featurePipeline as a geojson FeatureLists (downloads as a json)
* @param featurePipeline The FeaturePipeline you want to export
* @param options The options object
* @param options.metadata True if you want to include the MapComplete metadata, false otherwise
*/
public static extractGeoJson(featurePipeline: FeatureSource, options: { metadata?: boolean } = {}) {
let defaults = {
metadata: false,
}
options = Utils.setDefaults(options, defaults);
// Select all features, ignore the freshness and other data
let featureList: any[] = featurePipeline.features.data.map((feature) => feature.feature);
if (!options.metadata) {
for (let i = 0; i < featureList.length; i++) {
let feature = featureList[i];
for (let property in feature.properties) {
if (property[0] == "_") {
delete featureList[i]["properties"][property];
}
}
}
}
return {type: "FeatureCollection", features: featureList}
}
}

View file

@ -47,6 +47,7 @@ export class OsmConnection {
public auth;
public userDetails: UIEventSource<UserDetails>;
public isLoggedIn: UIEventSource<boolean>
private fakeUser: boolean;
_dryRun: boolean;
public preferencesHandler: OsmPreferences;
public changesetHandler: ChangesetHandler;
@ -59,12 +60,15 @@ export class OsmConnection {
url: string
};
constructor(dryRun: boolean, oauth_token: UIEventSource<string>,
constructor(dryRun: boolean,
fakeUser: boolean,
oauth_token: UIEventSource<string>,
// Used to keep multiple changesets open and to write to the correct changeset
layoutName: string,
singlePage: boolean = true,
osmConfiguration: "osm" | "osm-test" = 'osm'
) {
this.fakeUser = fakeUser;
this._singlePage = singlePage;
this._oauth_config = OsmConnection.oauth_configs[osmConfiguration] ?? OsmConnection.oauth_configs.osm;
console.debug("Using backend", this._oauth_config.url)
@ -72,7 +76,15 @@ export class OsmConnection {
this._iframeMode = Utils.runningFromConsole ? false : window !== window.top;
this.userDetails = new UIEventSource<UserDetails>(new UserDetails(this._oauth_config.url), "userDetails");
this.userDetails.data.dryRun = dryRun;
this.userDetails.data.dryRun = dryRun || fakeUser;
if(fakeUser){
const ud = this.userDetails.data;
ud.csCount = 5678
ud.loggedIn= true;
ud.unreadMessages = 0
ud.name = "Fake user"
ud.totalMessages = 42;
}
const self =this;
this.isLoggedIn = this.userDetails.map(user => user.loggedIn).addCallback(isLoggedIn => {
if(self.userDetails.data.loggedIn == false && isLoggedIn == true){
@ -138,6 +150,10 @@ export class OsmConnection {
}
public AttemptLogin() {
if(this.fakeUser){
console.log("AttemptLogin called, but ignored as fakeUser is set")
return;
}
const self = this;
console.log("Trying to log in...");
this.updateAuthObject();

View file

@ -20,6 +20,7 @@ import PendingChangesUploader from "./Logic/Actors/PendingChangesUploader";
import {Relation} from "./Logic/Osm/ExtractRelations";
import OsmApiFeatureSource from "./Logic/FeatureSource/OsmApiFeatureSource";
import ChangeToElementsActor from "./Logic/Actors/ChangeToElementsActor";
import FeaturePipeline from "./Logic/FeatureSource/FeaturePipeline";
/**
* Contains the global state: a bunch of UI-event sources
@ -59,8 +60,8 @@ export default class State {
public favouriteLayers: UIEventSource<string[]>;
public layerUpdater: OverpassFeatureSource;
public osmApiFeatureSource : OsmApiFeatureSource ;
public osmApiFeatureSource: OsmApiFeatureSource;
public filteredLayers: UIEventSource<{
@ -81,7 +82,7 @@ export default class State {
* Keeps track of relations: which way is part of which other way?
* Set by the overpass-updater; used in the metatagging
*/
public readonly knownRelations = new UIEventSource<Map<string, {role: string, relation: Relation}[]>>(undefined, "Relation memberships")
public readonly knownRelations = new UIEventSource<Map<string, { role: string, relation: Relation }[]>>(undefined, "Relation memberships")
public readonly featureSwitchUserbadge: UIEventSource<boolean>;
public readonly featureSwitchSearch: UIEventSource<boolean>;
@ -96,6 +97,11 @@ export default class State {
public readonly featureSwitchIsDebugging: UIEventSource<boolean>;
public readonly featureSwitchShowAllQuestions: UIEventSource<boolean>;
public readonly featureSwitchApiURL: UIEventSource<string>;
public readonly featureSwitchEnableExport: UIEventSource<boolean>;
public readonly featureSwitchFakeUser: UIEventSource<boolean>;
public readonly featurePipeline: FeaturePipeline;
/**
@ -126,7 +132,7 @@ export default class State {
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 >${Constants.userJourney.mapCompleteHelpUnlock} changesets)`).map<number>(
str => isNaN(Number(str)) ? 0 : Number(str), [], n => "" + n
);
constructor(layoutToUse: LayoutConfig) {
const self = this;
@ -200,16 +206,22 @@ export default class State {
"Disables/Enables the geolocation button");
this.featureSwitchShowAllQuestions = featSw("fs-all-questions", (layoutToUse) => layoutToUse?.enableShowAllQuestions ?? false,
"Always show all questions");
this.featureSwitchEnableExport = featSw("fs-export", (layoutToUse) => layoutToUse?.enableExportButton ?? false,
"If set, enables the 'download'-button to download everything as geojson")
this.featureSwitchIsTesting = 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")
.map(str => str === "true", [], b => "" + b);
this.featureSwitchIsDebugging = QueryParameters.GetQueryParameter("debug","false",
this.featureSwitchFakeUser = QueryParameters.GetQueryParameter("fake-user", "false",
"If true, 'dryrun' mode is activated and a fake user account is loaded")
.map(str => str === "true", [], b => "" + b);
this.featureSwitchIsDebugging = QueryParameters.GetQueryParameter("debug", "false",
"If true, shows some extra debugging help such as all the available tags on every object")
.map(str => str === "true", [], b => "" + b)
this.featureSwitchApiURL = QueryParameters.GetQueryParameter("backend","osm",
this.featureSwitchApiURL = QueryParameters.GetQueryParameter("backend", "osm",
"The OSM backend to use - can be used to redirect mapcomplete to the testing backend when using 'osm-test'")
}
@ -222,18 +234,19 @@ export default class State {
this.backgroundLayerId = QueryParameters.GetQueryParameter("background",
layoutToUse?.defaultBackgroundId ?? "osm",
"The id of the background layer to start with")
layoutToUse?.defaultBackgroundId ?? "osm",
"The id of the background layer to start with")
}
if(Utils.runningFromConsole){
if (Utils.runningFromConsole) {
return;
}
this.osmConnection = new OsmConnection(
this.featureSwitchIsTesting.data,
this.featureSwitchFakeUser.data,
QueryParameters.GetQueryParameter("oauth_token", undefined,
"Used to complete the login"),
layoutToUse?.id,
@ -249,7 +262,7 @@ export default class State {
new ChangeToElementsActor(this.changes, this.allElements)
this.osmApiFeatureSource = new OsmApiFeatureSource()
new PendingChangesUploader(this.changes, this.selectedElement);
this.mangroveIdentity = new MangroveIdentity(

2
Svg.ts
View file

@ -94,7 +94,7 @@ export default class Svg {
public static crosshair_empty_svg() { return new Img(Svg.crosshair_empty, true);}
public static crosshair_empty_ui() { return new FixedUiElement(Svg.crosshair_empty_img);}
public static crosshair_locked = " <!-- Created with Inkscape (http://www.inkscape.org/) --> <svg xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:cc=\"http://creativecommons.org/ns#\" xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\" xmlns:svg=\"http://www.w3.org/2000/svg\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:sodipodi=\"http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd\" xmlns:inkscape=\"http://www.inkscape.org/namespaces/inkscape\" width=\"100\" height=\"100\" viewBox=\"0 0 26.458333 26.458334\" version=\"1.1\" id=\"svg8\" inkscape:version=\"0.92.5 (2060ec1f9f, 2020-04-08)\" sodipodi:docname=\"crosshair-locked.svg\"> <defs id=\"defs2\" /> <sodipodi:namedview id=\"base\" pagecolor=\"#ffffff\" bordercolor=\"#666666\" borderopacity=\"1.0\" inkscape:pageopacity=\"0.0\" inkscape:pageshadow=\"2\" inkscape:zoom=\"2.8284271\" inkscape:cx=\"67.47399\" inkscape:cy=\"29.788021\" inkscape:document-units=\"px\" inkscape:current-layer=\"layer1\" showgrid=\"false\" units=\"px\" showguides=\"true\" inkscape:guide-bbox=\"true\" inkscape:window-width=\"1920\" inkscape:window-height=\"999\" inkscape:window-x=\"0\" inkscape:window-y=\"0\" inkscape:window-maximized=\"1\" inkscape:snap-global=\"false\"> <sodipodi:guide position=\"13.229167,23.859748\" orientation=\"1,0\" id=\"guide815\" inkscape:locked=\"false\" /> <sodipodi:guide position=\"14.944824,13.229167\" orientation=\"0,1\" id=\"guide817\" inkscape:locked=\"false\" /> </sodipodi:namedview> <metadata id=\"metadata5\"> <rdf:RDF> <cc:Work rdf:about=\"\"> <dc:format>image/svg+xml</dc:format> <dc:type rdf:resource=\"http://purl.org/dc/dcmitype/StillImage\" /> <dc:title /> </cc:Work> </rdf:RDF> </metadata> <g inkscape:label=\"Layer 1\" inkscape:groupmode=\"layer\" id=\"layer1\" transform=\"translate(0,-270.54165)\"> <circle style=\"fill: none !important;fill-opacity:1;stroke:#5555ec;stroke-width:2.64583335;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.98823529\" id=\"path815\" cx=\"13.16302\" cy=\"283.77081\" r=\"8.8715391\" /> <path style=\"fill: none !important;stroke:#5555ec;stroke-width:2.09723878;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.98823529\" d=\"M 3.2841366,283.77082 H 1.0418969\" id=\"path817\" inkscape:connector-curvature=\"0\" /> <path style=\"fill: none !important;stroke:#5555ec;stroke-width:2.11666679;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.98823529\" d=\"M 25.405696,283.77082 H 23.286471\" id=\"path817-3\" inkscape:connector-curvature=\"0\" /> <path style=\"fill: none !important;stroke:#5555ec;stroke-width:2.11666679;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.98823529\" d=\"m 13.229167,295.9489 v -2.11763\" id=\"path817-3-6\" inkscape:connector-curvature=\"0\" /> <path style=\"fill: none !important;stroke:#5555ec;stroke-width:2.11666668;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.98823529\" d=\"m 13.229167,275.05759 v -3.44507\" id=\"path817-3-6-7\" inkscape:connector-curvature=\"0\" /> <g id=\"g824\" transform=\"matrix(0.63953151,0,0,0.64343684,6.732623,276.71502)\" style=\"fill:#ffcc33\"> <path id=\"path822\" d=\"M 16.07,8 H 15 V 5 C 15,5 15,0 10,0 5,0 5,5 5,5 V 8 H 3.93 A 1.93,1.93 0 0 0 2,9.93 v 8.15 A 1.93,1.93 0 0 0 3.93,20 H 16.07 A 1.93,1.93 0 0 0 18,18.07 V 9.93 A 1.93,1.93 0 0 0 16.07,8 Z M 10,16 a 2,2 0 1 1 2,-2 2,2 0 0 1 -2,2 z M 13,8 H 7 V 5.5 C 7,4 7,2 10,2 c 3,0 3,2 3,3.5 z\" inkscape:connector-curvature=\"0\" /> </g> </g> </svg> "
public static crosshair_locked = " <!-- Created with Inkscape (http://www.inkscape.org/) --> <svg xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:cc=\"http://creativecommons.org/ns#\" xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\" xmlns:svg=\"http://www.w3.org/2000/svg\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:sodipodi=\"http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd\" xmlns:inkscape=\"http://www.inkscape.org/namespaces/inkscape\" width=\"100\" height=\"100\" viewBox=\"0 0 26.458333 26.458334\" version=\"1.1\" id=\"svg8\" inkscape:version=\"0.92.5 (2060ec1f9f, 2020-04-08)\" sodipodi:docname=\"crosshair-locked.svg\"> <defs id=\"defs2\" /> <sodipodi:namedview id=\"base\" pagecolor=\"#ffffff\" bordercolor=\"#666666\" borderopacity=\"1.0\" inkscape:pageopacity=\"0.0\" inkscape:pageshadow=\"2\" inkscape:zoom=\"5.6568542\" inkscape:cx=\"27.044982\" inkscape:cy=\"77.667126\" inkscape:document-units=\"px\" inkscape:current-layer=\"layer1\" showgrid=\"false\" units=\"px\" showguides=\"true\" inkscape:guide-bbox=\"true\" inkscape:window-width=\"1920\" inkscape:window-height=\"999\" inkscape:window-x=\"0\" inkscape:window-y=\"0\" inkscape:window-maximized=\"1\" inkscape:snap-global=\"false\"> <sodipodi:guide position=\"13.229167,23.859748\" orientation=\"1,0\" id=\"guide815\" inkscape:locked=\"false\" /> <sodipodi:guide position=\"14.944824,13.229167\" orientation=\"0,1\" id=\"guide817\" inkscape:locked=\"false\" /> </sodipodi:namedview> <metadata id=\"metadata5\"> <rdf:RDF> <cc:Work rdf:about=\"\"> <dc:format>image/svg+xml</dc:format> <dc:type rdf:resource=\"http://purl.org/dc/dcmitype/StillImage\" /> <dc:title /> </cc:Work> </rdf:RDF> </metadata> <g inkscape:label=\"Layer 1\" inkscape:groupmode=\"layer\" id=\"layer1\" transform=\"translate(0,-270.54165)\"> <g id=\"g827\"> <circle r=\"8.8715391\" cy=\"283.77081\" cx=\"13.16302\" id=\"path815\" style=\"fill: none !important;fill-opacity:1;stroke:#5555ec;stroke-width:2.64583335;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.98823529\" /> <path inkscape:connector-curvature=\"0\" id=\"path817\" d=\"M 3.2841366,283.77082 H 1.0418969\" style=\"fill: none !important;stroke:#5555ec;stroke-width:2.09723878;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.98823529\" /> <path inkscape:connector-curvature=\"0\" id=\"path817-3\" d=\"M 25.405696,283.77082 H 23.286471\" style=\"fill: none !important;stroke:#5555ec;stroke-width:2.11666679;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.98823529\" /> <path inkscape:connector-curvature=\"0\" id=\"path817-3-6\" d=\"m 13.229167,295.9489 v -2.11763\" style=\"fill: none !important;stroke:#5555ec;stroke-width:2.11666679;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.98823529\" /> <path inkscape:connector-curvature=\"0\" id=\"path817-3-6-7\" d=\"m 13.229167,275.05759 v -3.44507\" style=\"fill: none !important;stroke:#5555ec;stroke-width:2.11666668;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.98823529\" /> </g> <path style=\"fill:#5555ec;fill-opacity:0.98823529;stroke-width:0.6151033\" inkscape:connector-curvature=\"0\" d=\"m 16.850267,281.91543 h -0.65616 v -1.85094 c 0,0 0,-3.08489 -3.066169,-3.08489 -3.066169,0 -3.066169,3.08489 -3.066169,3.08489 v 1.85094 H 9.4056091 a 1.1835412,1.1907685 0 0 0 -1.1835412,1.19077 v 5.02838 a 1.1835412,1.1907685 0 0 0 1.1835412,1.1846 h 7.4446579 a 1.1835412,1.1907685 0 0 0 1.183541,-1.19078 v -5.0222 a 1.1835412,1.1907685 0 0 0 -1.183541,-1.19077 z m -3.722329,4.93583 a 1.2264675,1.233957 0 1 1 1.226468,-1.23395 1.2264675,1.233957 0 0 1 -1.226468,1.23395 z m 1.839702,-4.93583 h -3.679403 v -1.54245 c 0,-0.92546 0,-2.15942 1.839701,-2.15942 1.839702,0 1.839702,1.23396 1.839702,2.15942 z\" id=\"path822\" /> </g> </svg> "
public static crosshair_locked_img = Img.AsImageElement(Svg.crosshair_locked)
public static crosshair_locked_svg() { return new Img(Svg.crosshair_locked, true);}
public static crosshair_locked_ui() { return new FixedUiElement(Svg.crosshair_locked_img);}

View file

@ -83,7 +83,7 @@ export default class Minimap extends BaseUIElement {
doubleClickZoom: this._allowMoving,
keyboard: this._allowMoving,
touchZoom: this._allowMoving,
zoomAnimation: this._allowMoving,
// Disabling this breaks the geojson layer - don't ask me why! zoomAnimation: this._allowMoving,
fadeAnimation: this._allowMoving
});

View file

@ -0,0 +1,21 @@
import {SubtleButton} from "../Base/SubtleButton";
import Svg from "../../Svg";
import Translations from "../i18n/Translations";
import State from "../../State";
import {FeatureSourceUtils} from "../../Logic/FeatureSource/FeatureSource";
import {Utils} from "../../Utils";
import Combine from "../Base/Combine";
export class ExportDataButton extends Combine {
constructor() {
const t = Translations.t.general.download
const button = new SubtleButton(Svg.floppy_ui(), t.downloadGeojson.Clone().SetClass("font-bold"))
.onClick(() => {
const geojson = FeatureSourceUtils.extractGeoJson(State.state.featurePipeline)
const name = State.state.layoutToUse.data.id;
Utils.offerContentsAsDownloadableFile(JSON.stringify(geojson), `MapComplete_${name}_export_${new Date().toISOString().substr(0,19)}.geojson`);
})
super([button, t.licenseInfo.Clone().SetClass("link-underline")])
}
}

View file

@ -2,11 +2,12 @@ import State from "../../State";
import BackgroundSelector from "./BackgroundSelector";
import LayerSelection from "./LayerSelection";
import Combine from "../Base/Combine";
import {FixedUiElement} from "../Base/FixedUiElement";
import ScrollableFullScreen from "../Base/ScrollableFullScreen";
import Translations from "../i18n/Translations";
import {UIEventSource} from "../../Logic/UIEventSource";
import BaseUIElement from "../BaseUIElement";
import Toggle from "../Input/Toggle";
import {ExportDataButton} from "./ExportDataButton";
export default class LayerControlPanel extends ScrollableFullScreen {
@ -14,27 +15,34 @@ export default class LayerControlPanel extends ScrollableFullScreen {
super(LayerControlPanel.GenTitle, LayerControlPanel.GeneratePanel, "layers", isShown);
}
private static GenTitle():BaseUIElement {
private static GenTitle(): BaseUIElement {
return Translations.t.general.layerSelection.title.Clone().SetClass("text-2xl break-words font-bold p-2")
}
private static GeneratePanel() : BaseUIElement {
let layerControlPanel: BaseUIElement = new FixedUiElement("");
private static GeneratePanel(): BaseUIElement {
const elements: BaseUIElement[] = []
if (State.state.layoutToUse.data.enableBackgroundLayerSelection) {
layerControlPanel = new BackgroundSelector();
layerControlPanel.SetStyle("margin:1em");
layerControlPanel.onClick(() => {
const backgroundSelector = new BackgroundSelector();
backgroundSelector.SetStyle("margin:1em");
backgroundSelector.onClick(() => {
});
elements.push(backgroundSelector)
}
if (State.state.filteredLayers.data.length > 1) {
const layerSelection = new LayerSelection(State.state.filteredLayers);
layerSelection.onClick(() => {
});
layerControlPanel = new Combine([layerSelection, "<br/>", layerControlPanel]);
}
elements.push(new Toggle(
new LayerSelection(State.state.filteredLayers),
undefined,
State.state.filteredLayers.map(layers => layers.length > 1)
))
return layerControlPanel;
elements.push(new Toggle(
new ExportDataButton(),
undefined,
State.state.featureSwitchEnableExport
))
return new Combine(elements).SetClass("flex flex-col")
}
}

View file

@ -74,7 +74,6 @@ export default class LayerSelection extends Combine {
);
}
super(checkboxes)
this.SetStyle("display:flex;flex-direction:column;")

View file

@ -37,7 +37,7 @@ export default class FeatureInfoBox extends ScrollableFullScreen {
.SetClass("break-words font-bold sm:p-0.5 md:p-1 sm:p-1.5 md:p-2");
const titleIcons = new Combine(
layerConfig.titleIcons.map(icon => new TagRenderingAnswer(tags, icon,
"block w-8 h-8 align-baseline box-content sm:p-0.5")
"block w-8 h-8 align-baseline box-content sm:p-0.5", "width: 2rem;")
))
.SetClass("flex flex-row flex-wrap pt-0.5 sm:pt-1 items-center mr-2")

View file

@ -16,31 +16,31 @@ export default class TagRenderingAnswer extends VariableUiElement {
throw "Trying to generate a tagRenderingAnswer without configuration..."
}
super(tagsSource.map(tags => {
if(tags === undefined){
if (tags === undefined) {
return undefined;
}
if(configuration.condition){
if(!configuration.condition.matchesProperties(tags)){
if (configuration.condition) {
if (!configuration.condition.matchesProperties(tags)) {
return undefined;
}
}
const trs = Utils.NoNull(configuration.GetRenderValues(tags));
if(trs.length === 0){
return undefined;
}
const valuesToRender: BaseUIElement[] = trs.map(tr => new SubstitutedTranslation(tr, tagsSource))
if(valuesToRender.length === 1){
return valuesToRender[0];
}else if(valuesToRender.length > 1){
return new List(valuesToRender)
}
return undefined;
}).map((element : BaseUIElement) => element?.SetClass(contentClasses)?.SetStyle(contentStyle)))
this.SetClass("flex items-center flex-row text-lg link-underline tag-renering-answer")
const trs = Utils.NoNull(configuration.GetRenderValues(tags));
if (trs.length === 0) {
return undefined;
}
const valuesToRender: BaseUIElement[] = trs.map(tr => new SubstitutedTranslation(tr, tagsSource))
if (valuesToRender.length === 1) {
return valuesToRender[0];
} else if (valuesToRender.length > 1) {
return new List(valuesToRender)
}
return undefined;
}).map((element: BaseUIElement) => element?.SetClass(contentClasses)?.SetStyle(contentStyle)))
this.SetClass("flex items-center flex-row text-lg link-underline")
this.SetStyle("word-wrap: anywhere;");
}

View file

@ -461,5 +461,12 @@ export class Utils {
b: parseInt(hex.substr(5, 2), 16),
}
}
public static setDefaults(options, defaults){
for (let key in defaults){
if (!(key in options)) options[key] = defaults[key];
}
return options;
}
}

View file

@ -25,9 +25,9 @@
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="2.8284271"
inkscape:cx="67.47399"
inkscape:cy="29.788021"
inkscape:zoom="5.6568542"
inkscape:cx="27.044982"
inkscape:cy="77.667126"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
@ -68,40 +68,39 @@
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-270.54165)">
<circle
style="fill:none;fill-opacity:1;stroke:#5555ec;stroke-width:2.64583335;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.98823529"
id="path815"
cx="13.16302"
cy="283.77081"
r="8.8715391" />
<path
style="fill:none;stroke:#5555ec;stroke-width:2.09723878;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.98823529"
d="M 3.2841366,283.77082 H 1.0418969"
id="path817"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#5555ec;stroke-width:2.11666679;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.98823529"
d="M 25.405696,283.77082 H 23.286471"
id="path817-3"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#5555ec;stroke-width:2.11666679;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.98823529"
d="m 13.229167,295.9489 v -2.11763"
id="path817-3-6"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#5555ec;stroke-width:2.11666668;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.98823529"
d="m 13.229167,275.05759 v -3.44507"
id="path817-3-6-7"
inkscape:connector-curvature="0" />
<g
id="g824"
transform="matrix(0.63953151,0,0,0.64343684,6.732623,276.71502)"
style="fill:#ffcc33">
id="g827">
<circle
r="8.8715391"
cy="283.77081"
cx="13.16302"
id="path815"
style="fill:none;fill-opacity:1;stroke:#5555ec;stroke-width:2.64583335;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.98823529" />
<path
id="path822"
d="M 16.07,8 H 15 V 5 C 15,5 15,0 10,0 5,0 5,5 5,5 V 8 H 3.93 A 1.93,1.93 0 0 0 2,9.93 v 8.15 A 1.93,1.93 0 0 0 3.93,20 H 16.07 A 1.93,1.93 0 0 0 18,18.07 V 9.93 A 1.93,1.93 0 0 0 16.07,8 Z M 10,16 a 2,2 0 1 1 2,-2 2,2 0 0 1 -2,2 z M 13,8 H 7 V 5.5 C 7,4 7,2 10,2 c 3,0 3,2 3,3.5 z"
inkscape:connector-curvature="0" />
inkscape:connector-curvature="0"
id="path817"
d="M 3.2841366,283.77082 H 1.0418969"
style="fill:none;stroke:#5555ec;stroke-width:2.09723878;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.98823529" />
<path
inkscape:connector-curvature="0"
id="path817-3"
d="M 25.405696,283.77082 H 23.286471"
style="fill:none;stroke:#5555ec;stroke-width:2.11666679;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.98823529" />
<path
inkscape:connector-curvature="0"
id="path817-3-6"
d="m 13.229167,295.9489 v -2.11763"
style="fill:none;stroke:#5555ec;stroke-width:2.11666679;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.98823529" />
<path
inkscape:connector-curvature="0"
id="path817-3-6-7"
d="m 13.229167,275.05759 v -3.44507"
style="fill:none;stroke:#5555ec;stroke-width:2.11666668;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.98823529" />
</g>
<path
style="fill:#5555ec;fill-opacity:0.98823529;stroke-width:0.6151033"
inkscape:connector-curvature="0"
d="m 16.850267,281.91543 h -0.65616 v -1.85094 c 0,0 0,-3.08489 -3.066169,-3.08489 -3.066169,0 -3.066169,3.08489 -3.066169,3.08489 v 1.85094 H 9.4056091 a 1.1835412,1.1907685 0 0 0 -1.1835412,1.19077 v 5.02838 a 1.1835412,1.1907685 0 0 0 1.1835412,1.1846 h 7.4446579 a 1.1835412,1.1907685 0 0 0 1.183541,-1.19078 v -5.0222 a 1.1835412,1.1907685 0 0 0 -1.183541,-1.19077 z m -3.722329,4.93583 a 1.2264675,1.233957 0 1 1 1.226468,-1.23395 1.2264675,1.233957 0 0 1 -1.226468,1.23395 z m 1.839702,-4.93583 h -3.679403 v -1.54245 c 0,-0.92546 0,-2.15942 1.839701,-2.15942 1.839702,0 1.839702,1.23396 1.839702,2.15942 z"
id="path822" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

View file

@ -162,6 +162,18 @@
"license": "CC0; trivial",
"sources": []
},
{
"authors": [],
"path": "crosshair-empty.svg",
"license": "CC0; trivial",
"sources": []
},
{
"authors": [],
"path": "crosshair-locked.svg",
"license": "CC0; trivial",
"sources": []
},
{
"authors": [
"Dave Gandy"
@ -204,7 +216,7 @@
"license": "CC0",
"sources": [
"https://commons.wikimedia.org/wiki/File:Media-floppy.svg",
" http://tango.freedesktop.org/Tango_Desktop_Project"
"http://tango.freedesktop.org/Tango_Desktop_Project"
]
},
{
@ -582,5 +594,15 @@
"sources": [
"https://www.mapillary.com/"
]
},
{
"authors": [
"The Tango! Desktop Project"
],
"path": "floppy.svg",
"license": "CC0",
"sources": [
"https://commons.wikimedia.org/wiki/File:Media-floppy.svg"
]
}
]

View file

@ -157,6 +157,10 @@
"zoomInToSeeThisLayer": "Zoom in to see this layer",
"title": "Select layers"
},
"download": {
"downloadGeojson": "Download visible data as geojson",
"licenseInfo": "<h3>Copyright notice</h3>The provided is available under ODbL. Reusing this data is free for any purpose, but <ul><li>the attribution <b>© OpenStreetMap contributors</b></li><li>Any change to this data must be republished under the same license</li></ul>. Please see the full <a href='https://www.openstreetmap.org/copyright' target='_blank'>copyright notice</a> for details"
},
"weekdays": {
"abbreviations": {
"monday": "Mon",

View file

@ -12,7 +12,7 @@ import BaseUIElement from "./UI/BaseUIElement";
import Table from "./UI/Base/Table";
const connection = new OsmConnection(false, new UIEventSource<string>(undefined), "");
const connection = new OsmConnection(false, false, new UIEventSource<string>(undefined), "");
let rendered = false;

View file

@ -15,7 +15,7 @@ export default class OsmConnectionSpec extends T {
super("OsmConnectionSpec-test", [
["login on dev",
() => {
const osmConn = new OsmConnection(false,
const osmConn = new OsmConnection(false,false,
new UIEventSource<string>(undefined),
"Unit test",
true,