diff --git a/Customizations/JSON/TagRenderingConfig.ts b/Customizations/JSON/TagRenderingConfig.ts
index d3d440493..7b36dae44 100644
--- a/Customizations/JSON/TagRenderingConfig.ts
+++ b/Customizations/JSON/TagRenderingConfig.ts
@@ -27,7 +27,8 @@ export default class TagRenderingConfig {
readonly type: string,
readonly addExtraTags: TagsFilter[];
readonly inline: boolean,
- readonly default?: string
+ readonly default?: string,
+ readonly helperArgs?: (string | number | boolean)[]
};
readonly multiAnswer: boolean;
@@ -76,8 +77,8 @@ export default class TagRenderingConfig {
addExtraTags: json.freeform.addExtraTags?.map((tg, i) =>
FromJSON.Tag(tg, `${context}.extratag[${i}]`)) ?? [],
inline: json.freeform.inline ?? false,
- default: json.freeform.default
-
+ default: json.freeform.default,
+ helperArgs: json.freeform.helperArgs
}
if (json.freeform["extraTags"] !== undefined) {
@@ -336,20 +337,20 @@ export default class TagRenderingConfig {
* Note: this might be hidden by conditions
*/
public hasMinimap(): boolean {
- const translations : Translation[]= Utils.NoNull([this.render, ...(this.mappings ?? []).map(m => m.then)]);
+ const translations: Translation[] = Utils.NoNull([this.render, ...(this.mappings ?? []).map(m => m.then)]);
for (const translation of translations) {
for (const key in translation.translations) {
- if(!translation.translations.hasOwnProperty(key)){
+ if (!translation.translations.hasOwnProperty(key)) {
continue
}
const template = translation.translations[key]
const parts = SubstitutedTranslation.ExtractSpecialComponents(template)
- const hasMiniMap = parts.filter(part =>part.special !== undefined ).some(special => special.special.func.funcName === "minimap")
- if(hasMiniMap){
+ const hasMiniMap = parts.filter(part => part.special !== undefined).some(special => special.special.func.funcName === "minimap")
+ if (hasMiniMap) {
return true;
}
}
}
return false;
- }
+ }
}
\ No newline at end of file
diff --git a/Customizations/JSON/TagRenderingConfigJson.ts b/Customizations/JSON/TagRenderingConfigJson.ts
index 89871ec74..843889525 100644
--- a/Customizations/JSON/TagRenderingConfigJson.ts
+++ b/Customizations/JSON/TagRenderingConfigJson.ts
@@ -30,6 +30,7 @@ export interface TagRenderingConfigJson {
* Allow freeform text input from the user
*/
freeform?: {
+
/**
* If this key is present, then 'render' is used to display the value.
* If this is undefined, the rendering is _always_ shown
@@ -40,6 +41,11 @@ export interface TagRenderingConfigJson {
* See Docs/SpecialInputElements.md and UI/Input/ValidatedTextField.ts for supported values
*/
type?: string,
+ /**
+ * Extra parameters to initialize the input helper arguments.
+ * For semantics, see the 'SpecialInputElements.md'
+ */
+ helperArgs?: (string | number | boolean)[];
/**
* If a value is added with the textfield, these extra tag is addded.
* Useful to add a 'fixme=freeform textfield used - to be checked'
diff --git a/Svg.ts b/Svg.ts
index 26c5505ed..a3fe46cd7 100644
--- a/Svg.ts
+++ b/Svg.ts
@@ -184,7 +184,7 @@ export default class Svg {
public static layersAdd_svg() { return new Img(Svg.layersAdd, true);}
public static layersAdd_ui() { return new FixedUiElement(Svg.layersAdd_img);}
- public static length_crosshair = " "
+ public static length_crosshair = " "
public static length_crosshair_img = Img.AsImageElement(Svg.length_crosshair)
public static length_crosshair_svg() { return new Img(Svg.length_crosshair, true);}
public static length_crosshair_ui() { return new FixedUiElement(Svg.length_crosshair_img);}
diff --git a/UI/Base/Minimap.ts b/UI/Base/Minimap.ts
index a7066c9ee..6ebf37a75 100644
--- a/UI/Base/Minimap.ts
+++ b/UI/Base/Minimap.ts
@@ -5,6 +5,7 @@ import Loc from "../../Models/Loc";
import BaseLayer from "../../Models/BaseLayer";
import AvailableBaseLayers from "../../Logic/Actors/AvailableBaseLayers";
import {Map} from "leaflet";
+import {Utils} from "../../Utils";
export default class Minimap extends BaseUIElement {
@@ -15,11 +16,13 @@ export default class Minimap extends BaseUIElement {
private readonly _location: UIEventSource;
private _isInited = false;
private _allowMoving: boolean;
+ private readonly _leafletoptions: any;
constructor(options?: {
background?: UIEventSource,
location?: UIEventSource,
- allowMoving?: boolean
+ allowMoving?: boolean,
+ leafletOptions?: any
}
) {
super()
@@ -28,10 +31,11 @@ export default class Minimap extends BaseUIElement {
this._location = options?.location ?? new UIEventSource(undefined)
this._id = "minimap" + Minimap._nextId;
this._allowMoving = options.allowMoving ?? true;
+ this._leafletoptions = options.leafletOptions ?? {}
Minimap._nextId++
}
-
+
protected InnerConstructElement(): HTMLElement {
const div = document.createElement("div")
div.id = this._id;
@@ -52,7 +56,7 @@ export default class Minimap extends BaseUIElement {
return wrapper;
}
-
+
private InitMap() {
if (this._constructedHtmlElement === undefined) {
// This element isn't initialized yet
@@ -71,8 +75,8 @@ export default class Minimap extends BaseUIElement {
const location = this._location;
let currentLayer = this._background.data.layer()
- const map = L.map(this._id, {
- center: [location.data?.lat ?? 0, location.data?.lon ?? 0],
+ const options = {
+ center: <[number, number]> [location.data?.lat ?? 0, location.data?.lon ?? 0],
zoom: location.data?.zoom ?? 2,
layers: [currentLayer],
zoomControl: false,
@@ -82,9 +86,13 @@ export default class Minimap extends BaseUIElement {
doubleClickZoom: this._allowMoving,
keyboard: this._allowMoving,
touchZoom: this._allowMoving,
- // Disabling this breaks the geojson layer - don't ask me why! zoomAnimation: this._allowMoving,
+ // Disabling this breaks the geojson layer - don't ask me why! zoomAnimation: this._allowMoving,
fadeAnimation: this._allowMoving
- });
+ }
+
+ Utils.Merge(this._leafletoptions, options)
+
+ const map = L.map(this._id, options);
map.setMaxBounds(
[[-100, -200], [100, 200]]
diff --git a/UI/Input/LengthInput.ts b/UI/Input/LengthInput.ts
index 82b79ee0f..ea7530ce3 100644
--- a/UI/Input/LengthInput.ts
+++ b/UI/Input/LengthInput.ts
@@ -2,11 +2,12 @@ import {InputElement} from "./InputElement";
import {UIEventSource} from "../../Logic/UIEventSource";
import Combine from "../Base/Combine";
import Svg from "../../Svg";
-import BaseUIElement from "../BaseUIElement";
-import {FixedUiElement} from "../Base/FixedUiElement";
import {Utils} from "../../Utils";
import Loc from "../../Models/Loc";
-import Minimap from "../Base/Minimap";
+import {GeoOperations} from "../../Logic/GeoOperations";
+import DirectionInput from "./DirectionInput";
+import {RadioButton} from "./RadioButton";
+import {FixedInputElement} from "./FixedInputElement";
/**
@@ -19,13 +20,15 @@ export default class LengthInput extends InputElement {
private readonly value: UIEventSource;
private background;
- constructor(mapBackground: UIEventSource,
+ constructor(mapBackground: UIEventSource,
location: UIEventSource,
value?: UIEventSource) {
super();
this._location = location;
this.value = value ?? new UIEventSource(undefined);
this.background = mapBackground;
+ this.SetClass("block")
+
}
GetValue(): UIEventSource {
@@ -33,83 +36,150 @@ export default class LengthInput extends InputElement {
}
IsValid(str: string): boolean {
- const t = Number(str);
+ const t = Number(str)
return !isNaN(t) && t >= 0 && t <= 360;
}
protected InnerConstructElement(): HTMLElement {
-
- let map: BaseUIElement = new FixedUiElement("")
+ const modeElement = new RadioButton([
+ new FixedInputElement("Measure", "measure"),
+ new FixedInputElement("Move", "move")
+ ])
+ // @ts-ignore
+ let map = undefined
if (!Utils.runningFromConsole) {
- map = new Minimap({
+ map = DirectionInput.constructMinimap({
background: this.background,
- allowMoving: true,
- location: this._location
+ allowMoving: false,
+ location: this._location,
+ leafletOptions: {
+ tap: true
+ }
})
}
-
const element = new Combine([
- Svg.direction_stroke_svg().SetStyle(
- `position: absolute;top: 0;left: 0;width: 100%;height: 100%;transform:rotate(${this.value.data ?? 0}deg);`)
- .SetClass("direction-svg relative")
- .SetStyle("z-index: 1000"),
- map.SetClass("w-full h-full absolute top-0 left-O rounded-full overflow-hidden"),
+ new Combine([Svg.length_crosshair_ui().SetStyle(
+ `visibility: hidden; position: absolute;top: 0;left: 0;transform:rotate(${this.value.data ?? 0}deg);`)
+ ])
+ .SetClass("block length-crosshair-svg relative")
+ .SetStyle("z-index: 1000"),
+ map?.SetClass("w-full h-full block absolute top-0 left-O overflow-hidden"),
])
- .SetStyle("position:relative;display:block;width: min(100%, 25em); height: min(100% , 25em); background:white; border: 1px solid black; border-radius: 999em")
+ .SetClass("relative block bg-white border border-black rounded-3xl overflow-hidden")
.ConstructElement()
- this.value.addCallbackAndRunD(rotation => {
- const cone = element.getElementsByClassName("direction-svg")[0] as HTMLElement
- cone.style.transform = `rotate(${rotation}deg)`;
-
- })
-
- this.RegisterTriggers(element)
+ this.RegisterTriggers(element, map?.leafletMap)
element.style.overflow = "hidden"
-
- return element;
+ element.style.display = "block"
+
+ return element
}
- private RegisterTriggers(htmlElement: HTMLElement) {
- const self = this;
+ private RegisterTriggers(htmlElement: HTMLElement, leafletMap: UIEventSource) {
+
+ let firstClickXY: [number, number] = undefined
+ let lastClickXY: [number, number] = undefined
+ const self = this;
+
+
+ function onPosChange(x: number, y: number, isDown: boolean, isUp?: boolean) {
+ if (x === undefined || y === undefined) {
+ // Touch end
+ firstClickXY = undefined;
+ lastClickXY = undefined;
+ return;
+ }
- function onPosChange(x: number, y: number) {
const rect = htmlElement.getBoundingClientRect();
- const dx = -(rect.left + rect.right) / 2 + x;
- const dy = (rect.top + rect.bottom) / 2 - y;
- const angle = 180 * Math.atan2(dy, dx) / Math.PI;
- const angleGeo = Math.floor((450 - angle) % 360);
- self.value.setData("" + angleGeo)
+ // From the central part of location
+ const dx = x - rect.left;
+ const dy = y - rect.top;
+ if (isDown) {
+ if (lastClickXY === undefined && firstClickXY === undefined) {
+ firstClickXY = [dx, dy];
+ } else if (firstClickXY !== undefined && lastClickXY === undefined) {
+ lastClickXY = [dx, dy]
+ } else if (firstClickXY !== undefined && lastClickXY !== undefined) {
+ // we measure again
+ firstClickXY = [dx, dy]
+ lastClickXY = undefined;
+ }
+ }
+ if (isUp) {
+ const distance = Math.sqrt((dy - firstClickXY[1]) * (dy - firstClickXY[1]) + (dx - firstClickXY[0]) * (dx - firstClickXY[0]))
+ if (distance > 15) {
+ lastClickXY = [dx, dy]
+ }
+
+
+ } else if (lastClickXY !== undefined) {
+ return;
+ }
+
+
+ const measurementCrosshair = htmlElement.getElementsByClassName("length-crosshair-svg")[0] as HTMLElement
+ const measurementCrosshairInner: HTMLElement = measurementCrosshair.firstChild
+ if (firstClickXY === undefined) {
+ measurementCrosshair.style.visibility = "hidden"
+ } else {
+ measurementCrosshair.style.visibility = "unset"
+ measurementCrosshair.style.left = firstClickXY[0] + "px";
+ measurementCrosshair.style.top = firstClickXY[1] + "px"
+
+ const angle = 180 * Math.atan2(firstClickXY[1] - dy, firstClickXY[0] - dx) / Math.PI;
+ const angleGeo = (angle + 270) % 360
+ measurementCrosshairInner.style.transform = `rotate(${angleGeo}deg)`;
+
+ const distance = Math.sqrt((dy - firstClickXY[1]) * (dy - firstClickXY[1]) + (dx - firstClickXY[0]) * (dx - firstClickXY[0]))
+ measurementCrosshairInner.style.width = (distance * 2) + "px"
+ measurementCrosshairInner.style.marginLeft = -distance + "px"
+ measurementCrosshairInner.style.marginTop = -distance + "px"
+
+
+ const leaflet = leafletMap?.data
+ if (leaflet) {
+ console.log(firstClickXY, [dx, dy], "pixel origin", leaflet.getPixelOrigin())
+ const first = leaflet.layerPointToLatLng(firstClickXY)
+ const last = leaflet.layerPointToLatLng([dx, dy])
+ console.log(first, last)
+ const geoDist = Math.floor(GeoOperations.distanceBetween([first.lng, first.lat], [last.lng, last.lat]) * 100000) / 100
+ console.log("First", first, "last", last, "d", geoDist)
+ self.value.setData("" + geoDist)
+ }
+
+ }
+
}
- htmlElement.ontouchmove = (ev: TouchEvent) => {
- onPosChange(ev.touches[0].clientX, ev.touches[0].clientY);
+ htmlElement.ontouchstart = (ev: TouchEvent) => {
+ onPosChange(ev.touches[0].clientX, ev.touches[0].clientY, true);
ev.preventDefault();
}
- htmlElement.ontouchstart = (ev: TouchEvent) => {
- onPosChange(ev.touches[0].clientX, ev.touches[0].clientY);
+ htmlElement.ontouchmove = (ev: TouchEvent) => {
+ onPosChange(ev.touches[0].clientX, ev.touches[0].clientY, false);
+ ev.preventDefault();
}
- let isDown = false;
+ htmlElement.ontouchend = (ev: TouchEvent) => {
+ onPosChange(undefined, undefined, false, true);
+ ev.preventDefault();
+ }
htmlElement.onmousedown = (ev: MouseEvent) => {
- isDown = true;
- onPosChange(ev.clientX, ev.clientY);
+ onPosChange(ev.clientX, ev.clientY, true);
ev.preventDefault();
}
htmlElement.onmouseup = (ev) => {
- isDown = false;
+ onPosChange(ev.clientX, ev.clientY, false, true);
ev.preventDefault();
}
htmlElement.onmousemove = (ev: MouseEvent) => {
- if (isDown) {
- onPosChange(ev.clientX, ev.clientY);
- }
+ onPosChange(ev.clientX, ev.clientY, false);
ev.preventDefault();
}
}
diff --git a/UI/Input/ValidatedTextField.ts b/UI/Input/ValidatedTextField.ts
index 2eeff8a54..809ff025f 100644
--- a/UI/Input/ValidatedTextField.ts
+++ b/UI/Input/ValidatedTextField.ts
@@ -13,6 +13,8 @@ import {Utils} from "../../Utils";
import Loc from "../../Models/Loc";
import {Unit} from "../../Customizations/JSON/Denomination";
import BaseUIElement from "../BaseUIElement";
+import LengthInput from "./LengthInput";
+import {GeoOperations} from "../../Logic/GeoOperations";
interface TextFieldDef {
name: string,
@@ -21,14 +23,16 @@ interface TextFieldDef {
reformat?: ((s: string, country?: () => string) => string),
inputHelper?: (value: UIEventSource, options?: {
location: [number, number],
- mapBackgroundLayer?: UIEventSource
+ mapBackgroundLayer?: UIEventSource,
+ args: (string | number | boolean)[]
+ feature?: any
}) => InputElement,
-
inputmode?: string
}
export default class ValidatedTextField {
+ public static bestLayerAt: (location: UIEventSource, preferences: UIEventSource) => any
public static tpList: TextFieldDef[] = [
ValidatedTextField.tp(
@@ -63,6 +67,79 @@ export default class ValidatedTextField {
return [year, month, day].join('-');
},
(value) => new SimpleDatePicker(value)),
+ ValidatedTextField.tp(
+ "direction",
+ "A geographical direction, in degrees. 0° is north, 90° is east, ... Will return a value between 0 (incl) and 360 (excl)",
+ (str) => {
+ str = "" + str;
+ return str !== undefined && str.indexOf(".") < 0 && !isNaN(Number(str)) && Number(str) >= 0 && Number(str) <= 360
+ }, str => str,
+ (value, options) => {
+ const args = options.args ?? []
+ let zoom = 19
+ if (args[0]) {
+ zoom = Number(args[0])
+ if (isNaN(zoom)) {
+ throw "Invalid zoom level for argument at 'length'-input"
+ }
+ }
+ const location = new UIEventSource({
+ lat: options.location[0],
+ lon: options.location[1],
+ zoom: zoom
+ })
+ if (args[1]) {
+ // We have a prefered map!
+ options.mapBackgroundLayer = ValidatedTextField.bestLayerAt(
+ location, new UIEventSource(args[1].split(","))
+ )
+ }
+ const di = new DirectionInput(options.mapBackgroundLayer, location, value)
+ di.SetStyle("height: 20rem;");
+
+ return di;
+ },
+ "numeric"
+ ),
+ ValidatedTextField.tp(
+ "length",
+ "A geographical length in meters (rounded at two points). Will give an extra minimap with a measurement tool. Arguments: [ zoomlevel, preferredBackgroundMapType (comma seperated) ], e.g. `[\"21\", \"map,photo\"]",
+ (str) => {
+ const t = Number(str)
+ return !isNaN(t)
+ },
+ str => str,
+ (value, options) => {
+ const args = options.args ?? []
+ let zoom = 19
+ if (args[0]) {
+ zoom = Number(args[0])
+ if (isNaN(zoom)) {
+ throw "Invalid zoom level for argument at 'length'-input"
+ }
+ }
+
+ // Bit of a hack: we project the centerpoint to the closes point on the road - if available
+ if(options.feature){
+ }
+ options.feature
+
+ const location = new UIEventSource({
+ lat: options.location[0],
+ lon: options.location[1],
+ zoom: zoom
+ })
+ if (args[1]) {
+ // We have a prefered map!
+ options.mapBackgroundLayer = ValidatedTextField.bestLayerAt(
+ location, new UIEventSource(args[1].split(","))
+ )
+ }
+ const li = new LengthInput(options.mapBackgroundLayer, location, value)
+ li.SetStyle("height: 20rem;")
+ return li;
+ }
+ ),
ValidatedTextField.tp(
"wikidata",
"A wikidata identifier, e.g. Q42",
@@ -113,22 +190,6 @@ export default class ValidatedTextField {
undefined,
undefined,
"numeric"),
- ValidatedTextField.tp(
- "direction",
- "A geographical direction, in degrees. 0° is north, 90° is east, ... Will return a value between 0 (incl) and 360 (excl)",
- (str) => {
- str = "" + str;
- return str !== undefined && str.indexOf(".") < 0 && !isNaN(Number(str)) && Number(str) >= 0 && Number(str) <= 360
- }, str => str,
- (value, options) => {
- return new DirectionInput(options.mapBackgroundLayer , new UIEventSource({
- lat: options.location[0],
- lon: options.location[1],
- zoom: 19
- }),value);
- },
- "numeric"
- ),
ValidatedTextField.tp(
"float",
"A decimal",
@@ -222,6 +283,7 @@ export default class ValidatedTextField {
* {string (typename) --> TextFieldDef}
*/
public static AllTypes = ValidatedTextField.allTypesDict();
+
public static InputForType(type: string, options?: {
placeholder?: string | BaseUIElement,
value?: UIEventSource,
@@ -233,7 +295,9 @@ export default class ValidatedTextField {
country?: () => string,
location?: [number /*lat*/, number /*lon*/],
mapBackgroundLayer?: UIEventSource,
- unit?: Unit
+ unit?: Unit,
+ args?: (string | number | boolean)[] // Extra arguments for the inputHelper,
+ feature?: any
}): InputElement {
options = options ?? {};
options.placeholder = options.placeholder ?? type;
@@ -247,7 +311,7 @@ export default class ValidatedTextField {
if (str === undefined) {
return false;
}
- if(options.unit) {
+ if (options.unit) {
str = options.unit.stripUnitParts(str)
}
return isValidTp(str, country ?? options.country) && optValid(str, country ?? options.country);
@@ -268,7 +332,7 @@ export default class ValidatedTextField {
})
}
- if(options.unit) {
+ if (options.unit) {
// We need to apply a unit.
// This implies:
// We have to create a dropdown with applicable denominations, and fuse those values
@@ -288,17 +352,16 @@ export default class ValidatedTextField {
input,
unitDropDown,
// combine the value from the textfield and the dropdown into the resulting value that should go into OSM
- (text, denom) => denom?.canonicalValue(text, true) ?? undefined,
+ (text, denom) => denom?.canonicalValue(text, true) ?? undefined,
(valueWithDenom: string) => {
// Take the value from OSM and feed it into the textfield and the dropdown
const withDenom = unit.findDenomination(valueWithDenom);
- if(withDenom === undefined)
- {
+ if (withDenom === undefined) {
// Not a valid value at all - we give it undefined and leave the details up to the other elements
return [undefined, undefined]
}
const [strippedText, denom] = withDenom
- if(strippedText === undefined){
+ if (strippedText === undefined) {
return [undefined, undefined]
}
return [strippedText, denom]
@@ -306,18 +369,20 @@ export default class ValidatedTextField {
).SetClass("flex")
}
if (tp.inputHelper) {
- const helper = tp.inputHelper(input.GetValue(), {
+ const helper = tp.inputHelper(input.GetValue(), {
location: options.location,
- mapBackgroundLayer: options.mapBackgroundLayer
-
+ mapBackgroundLayer: options.mapBackgroundLayer,
+ args: options.args,
+ feature: options.feature
})
input = new CombinedInputElement(input, helper,
(a, _) => a, // We can ignore b, as they are linked earlier
a => [a, a]
- );
+ );
}
return input;
}
+
public static HelpText(): string {
const explanations = ValidatedTextField.tpList.map(type => ["## " + type.name, "", type.explanation].join("\n")).join("\n\n")
return "# Available types for text fields\n\nThe listed types here trigger a special input element. Use them in `tagrendering.freeform.type` of your tagrendering to activate them\n\n" + explanations
@@ -329,7 +394,9 @@ export default class ValidatedTextField {
reformat?: ((s: string, country?: () => string) => string),
inputHelper?: (value: UIEventSource, options?: {
location: [number, number],
- mapBackgroundLayer: UIEventSource
+ mapBackgroundLayer: UIEventSource,
+ args: string[],
+ feature: any
}) => InputElement,
inputmode?: string): TextFieldDef {
diff --git a/UI/Popup/TagRenderingQuestion.ts b/UI/Popup/TagRenderingQuestion.ts
index 20c0b00d2..c72375959 100644
--- a/UI/Popup/TagRenderingQuestion.ts
+++ b/UI/Popup/TagRenderingQuestion.ts
@@ -330,12 +330,15 @@ export default class TagRenderingQuestion extends Combine {
}
const tagsData = tags.data;
+ const feature = State.state.allElements.ContainingFeatures.get(tagsData.id)
const input: InputElement = ValidatedTextField.InputForType(configuration.freeform.type, {
isValid: (str) => (str.length <= 255),
country: () => tagsData._country,
location: [tagsData._lat, tagsData._lon],
mapBackgroundLayer: State.state.backgroundLayer,
- unit: applicableUnit
+ unit: applicableUnit,
+ args: configuration.freeform.helperArgs,
+ feature: feature
});
input.GetValue().setData(tagsData[freeform.key] ?? freeform.default);
diff --git a/UI/SpecialVisualizations.ts b/UI/SpecialVisualizations.ts
index 309060b36..5a38e8184 100644
--- a/UI/SpecialVisualizations.ts
+++ b/UI/SpecialVisualizations.ts
@@ -39,7 +39,8 @@ export default class SpecialVisualizations {
static constructMiniMap: (options?: {
background?: UIEventSource,
location?: UIEventSource,
- allowMoving?: boolean
+ allowMoving?: boolean,
+ leafletOptions?: any
}) => BaseUIElement;
static constructShowDataLayer: (features: UIEventSource<{ feature: any; freshness: Date }[]>, leafletMap: UIEventSource, layoutToUse: UIEventSource, enablePopups?: boolean, zoomToFeatures?: boolean) => any;
public static specialVisualizations: SpecialVisualization[] =
diff --git a/assets/svg/length-crosshair.svg b/assets/svg/length-crosshair.svg
index 6db2cf72b..cb83789fb 100644
--- a/assets/svg/length-crosshair.svg
+++ b/assets/svg/length-crosshair.svg
@@ -26,17 +26,17 @@
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
- inkscape:window-width="1680"
- inkscape:window-height="1009"
+ inkscape:window-width="1920"
+ inkscape:window-height="999"
id="namedview16"
showgrid="false"
showguides="true"
inkscape:guide-bbox="true"
- inkscape:zoom="0.25"
- inkscape:cx="-448.31847"
- inkscape:cy="144.08448"
+ inkscape:zoom="0.5"
+ inkscape:cx="108.3764"
+ inkscape:cy="623.05359"
inkscape:window-x="0"
- inkscape:window-y="15"
+ inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg14"
inkscape:snap-smooth-nodes="true" />
@@ -54,20 +54,36 @@ Created by potrace 1.15, written by Peter Selinger 2001-2017
+ ry="427.81949"
+ transform="rotate(-90)" />
+
+
+
diff --git a/assets/themes/widths/width.json b/assets/themes/widths/width.json
index 48a1e883a..298b9a128 100644
--- a/assets/themes/widths/width.json
+++ b/assets/themes/widths/width.json
@@ -64,7 +64,13 @@
},
"tagRenderings": [
{
- "render": "Deze straat is {width:carriageway}m breed"
+ "render": "Deze straat is {width:carriageway}m breed",
+ "question": "Hoe breed is deze straat?",
+ "freeform": {
+ "key": "width:carriageway",
+ "type": "length",
+ "helperArgs": [21, "map"]
+ }
},
{
"render": "Deze straat heeft {_width:difference}m te weinig:",
diff --git a/index.ts b/index.ts
index 70b06bf30..634ad8533 100644
--- a/index.ts
+++ b/index.ts
@@ -19,10 +19,13 @@ import DirectionInput from "./UI/Input/DirectionInput";
import SpecialVisualizations from "./UI/SpecialVisualizations";
import ShowDataLayer from "./UI/ShowDataLayer";
import * as L from "leaflet";
+import ValidatedTextField from "./UI/Input/ValidatedTextField";
+import AvailableBaseLayers from "./Logic/Actors/AvailableBaseLayers";
// Workaround for a stupid crash: inject some functions which would give stupid circular dependencies or crash the other nodejs scripts
SimpleMetaTagger.coder = new CountryCoder("https://pietervdvn.github.io/latlon2country/");
DirectionInput.constructMinimap = options => new Minimap(options)
+ValidatedTextField.bestLayerAt = (location, layerPref) => AvailableBaseLayers.SelectBestLayerAccordingTo(location, layerPref)
SpecialVisualizations.constructMiniMap = options => new Minimap(options)
SpecialVisualizations.constructShowDataLayer = (features: UIEventSource<{ feature: any, freshness: Date }[]>,
leafletMap: UIEventSource,
diff --git a/test.ts b/test.ts
index 23da820f3..21ca94b74 100644
--- a/test.ts
+++ b/test.ts
@@ -11,6 +11,7 @@ import LocationInput from "./UI/Input/LocationInput";
import Loc from "./Models/Loc";
import {VariableUiElement} from "./UI/Base/VariableUIElement";
import LengthInput from "./UI/Input/LengthInput";
+import AvailableBaseLayers from "./Logic/Actors/AvailableBaseLayers";
/*import ValidatedTextField from "./UI/Input/ValidatedTextField";
import Combine from "./UI/Base/Combine";
import {VariableUiElement} from "./UI/Base/VariableUIElement";
@@ -153,8 +154,16 @@ function TestMiniMap() {
}
//*/
-const li = new LengthInput()
- li.SetStyle("height: 20rem")
+const loc = new UIEventSource({
+ zoom: 24,
+ lat: 51.21043,
+ lon: 3.21389
+})
+const li = new LengthInput(
+ AvailableBaseLayers.SelectBestLayerAccordingTo(loc, new UIEventSource("map","photo")),
+ loc
+)
+ li.SetStyle("height: 30rem; background: aliceblue;")
.AttachTo("maindiv")
new VariableUiElement(li.GetValue().map(v => JSON.stringify(v, null, " "))).AttachTo("extradiv")
\ No newline at end of file