diff --git a/Svg.ts b/Svg.ts
index 0266e43e8e..8b1e293075 100644
--- a/Svg.ts
+++ b/Svg.ts
@@ -184,6 +184,11 @@ 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_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);}
+
public static logo = " "
public static logo_img = Img.AsImageElement(Svg.logo)
public static logo_svg() { return new Img(Svg.logo, true);}
@@ -344,4 +349,4 @@ export default class Svg {
public static wikipedia_svg() { return new Img(Svg.wikipedia, true);}
public static wikipedia_ui() { return new FixedUiElement(Svg.wikipedia_img);}
-public static All = {"SocialImageForeground.svg": Svg.SocialImageForeground,"add.svg": Svg.add,"addSmall.svg": Svg.addSmall,"ampersand.svg": Svg.ampersand,"arrow-left-smooth.svg": Svg.arrow_left_smooth,"arrow-right-smooth.svg": Svg.arrow_right_smooth,"back.svg": Svg.back,"bug.svg": Svg.bug,"camera-plus.svg": Svg.camera_plus,"checkmark.svg": Svg.checkmark,"circle.svg": Svg.circle,"clock.svg": Svg.clock,"close.svg": Svg.close,"compass.svg": Svg.compass,"cross_bottom_right.svg": Svg.cross_bottom_right,"crosshair-blue-center.svg": Svg.crosshair_blue_center,"crosshair-blue.svg": Svg.crosshair_blue,"crosshair-empty.svg": Svg.crosshair_empty,"crosshair-locked.svg": Svg.crosshair_locked,"crosshair.svg": Svg.crosshair,"delete_icon.svg": Svg.delete_icon,"direction.svg": Svg.direction,"direction_gradient.svg": Svg.direction_gradient,"direction_masked.svg": Svg.direction_masked,"direction_outline.svg": Svg.direction_outline,"direction_stroke.svg": Svg.direction_stroke,"down.svg": Svg.down,"envelope.svg": Svg.envelope,"floppy.svg": Svg.floppy,"gear.svg": Svg.gear,"help.svg": Svg.help,"home.svg": Svg.home,"home_white_bg.svg": Svg.home_white_bg,"josm_logo.svg": Svg.josm_logo,"layers.svg": Svg.layers,"layersAdd.svg": Svg.layersAdd,"logo.svg": Svg.logo,"logout.svg": Svg.logout,"mapcomplete_logo.svg": Svg.mapcomplete_logo,"mapillary.svg": Svg.mapillary,"mapillary_black.svg": Svg.mapillary_black,"min.svg": Svg.min,"no_checkmark.svg": Svg.no_checkmark,"or.svg": Svg.or,"osm-copyright.svg": Svg.osm_copyright,"osm-logo-us.svg": Svg.osm_logo_us,"osm-logo.svg": Svg.osm_logo,"pencil.svg": Svg.pencil,"phone.svg": Svg.phone,"pin.svg": Svg.pin,"plus.svg": Svg.plus,"pop-out.svg": Svg.pop_out,"reload.svg": Svg.reload,"ring.svg": Svg.ring,"search.svg": Svg.search,"send_email.svg": Svg.send_email,"share.svg": Svg.share,"square.svg": Svg.square,"star.svg": Svg.star,"star_half.svg": Svg.star_half,"star_outline.svg": Svg.star_outline,"star_outline_half.svg": Svg.star_outline_half,"statistics.svg": Svg.statistics,"translate.svg": Svg.translate,"up.svg": Svg.up,"wikidata.svg": Svg.wikidata,"wikimedia-commons-white.svg": Svg.wikimedia_commons_white,"wikipedia.svg": Svg.wikipedia};}
+public static All = {"SocialImageForeground.svg": Svg.SocialImageForeground,"add.svg": Svg.add,"addSmall.svg": Svg.addSmall,"ampersand.svg": Svg.ampersand,"arrow-left-smooth.svg": Svg.arrow_left_smooth,"arrow-right-smooth.svg": Svg.arrow_right_smooth,"back.svg": Svg.back,"bug.svg": Svg.bug,"camera-plus.svg": Svg.camera_plus,"checkmark.svg": Svg.checkmark,"circle.svg": Svg.circle,"clock.svg": Svg.clock,"close.svg": Svg.close,"compass.svg": Svg.compass,"cross_bottom_right.svg": Svg.cross_bottom_right,"crosshair-blue-center.svg": Svg.crosshair_blue_center,"crosshair-blue.svg": Svg.crosshair_blue,"crosshair-empty.svg": Svg.crosshair_empty,"crosshair-locked.svg": Svg.crosshair_locked,"crosshair.svg": Svg.crosshair,"delete_icon.svg": Svg.delete_icon,"direction.svg": Svg.direction,"direction_gradient.svg": Svg.direction_gradient,"direction_masked.svg": Svg.direction_masked,"direction_outline.svg": Svg.direction_outline,"direction_stroke.svg": Svg.direction_stroke,"down.svg": Svg.down,"envelope.svg": Svg.envelope,"floppy.svg": Svg.floppy,"gear.svg": Svg.gear,"help.svg": Svg.help,"home.svg": Svg.home,"home_white_bg.svg": Svg.home_white_bg,"josm_logo.svg": Svg.josm_logo,"layers.svg": Svg.layers,"layersAdd.svg": Svg.layersAdd,"length-crosshair.svg": Svg.length_crosshair,"logo.svg": Svg.logo,"logout.svg": Svg.logout,"mapcomplete_logo.svg": Svg.mapcomplete_logo,"mapillary.svg": Svg.mapillary,"mapillary_black.svg": Svg.mapillary_black,"min.svg": Svg.min,"no_checkmark.svg": Svg.no_checkmark,"or.svg": Svg.or,"osm-copyright.svg": Svg.osm_copyright,"osm-logo-us.svg": Svg.osm_logo_us,"osm-logo.svg": Svg.osm_logo,"pencil.svg": Svg.pencil,"phone.svg": Svg.phone,"pin.svg": Svg.pin,"plus.svg": Svg.plus,"pop-out.svg": Svg.pop_out,"reload.svg": Svg.reload,"ring.svg": Svg.ring,"search.svg": Svg.search,"send_email.svg": Svg.send_email,"share.svg": Svg.share,"square.svg": Svg.square,"star.svg": Svg.star,"star_half.svg": Svg.star_half,"star_outline.svg": Svg.star_outline,"star_outline_half.svg": Svg.star_outline_half,"statistics.svg": Svg.statistics,"translate.svg": Svg.translate,"up.svg": Svg.up,"wikidata.svg": Svg.wikidata,"wikimedia-commons-white.svg": Svg.wikimedia_commons_white,"wikipedia.svg": Svg.wikipedia};}
diff --git a/UI/Input/LengthInput.ts b/UI/Input/LengthInput.ts
new file mode 100644
index 0000000000..82b79ee0f4
--- /dev/null
+++ b/UI/Input/LengthInput.ts
@@ -0,0 +1,117 @@
+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";
+
+
+/**
+ * Selects a length after clicking on the minimap, in meters
+ */
+export default class LengthInput extends InputElement {
+ private readonly _location: UIEventSource;
+
+ public readonly IsSelected: UIEventSource = new UIEventSource(false);
+ private readonly value: UIEventSource;
+ private background;
+
+ constructor(mapBackground: UIEventSource,
+ location: UIEventSource,
+ value?: UIEventSource) {
+ super();
+ this._location = location;
+ this.value = value ?? new UIEventSource(undefined);
+ this.background = mapBackground;
+ }
+
+ GetValue(): UIEventSource {
+ return this.value;
+ }
+
+ IsValid(str: string): boolean {
+ const t = Number(str);
+ return !isNaN(t) && t >= 0 && t <= 360;
+ }
+
+ protected InnerConstructElement(): HTMLElement {
+
+ let map: BaseUIElement = new FixedUiElement("")
+ if (!Utils.runningFromConsole) {
+ map = new Minimap({
+ background: this.background,
+ allowMoving: true,
+ location: this._location
+ })
+ }
+
+ 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"),
+ ])
+ .SetStyle("position:relative;display:block;width: min(100%, 25em); height: min(100% , 25em); background:white; border: 1px solid black; border-radius: 999em")
+ .ConstructElement()
+
+
+ this.value.addCallbackAndRunD(rotation => {
+ const cone = element.getElementsByClassName("direction-svg")[0] as HTMLElement
+ cone.style.transform = `rotate(${rotation}deg)`;
+
+ })
+
+ this.RegisterTriggers(element)
+ element.style.overflow = "hidden"
+
+ return element;
+ }
+
+ private RegisterTriggers(htmlElement: HTMLElement) {
+ const self = this;
+
+ 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)
+ }
+
+
+ htmlElement.ontouchmove = (ev: TouchEvent) => {
+ onPosChange(ev.touches[0].clientX, ev.touches[0].clientY);
+ ev.preventDefault();
+ }
+
+ htmlElement.ontouchstart = (ev: TouchEvent) => {
+ onPosChange(ev.touches[0].clientX, ev.touches[0].clientY);
+ }
+
+ let isDown = false;
+
+ htmlElement.onmousedown = (ev: MouseEvent) => {
+ isDown = true;
+ onPosChange(ev.clientX, ev.clientY);
+ ev.preventDefault();
+ }
+
+ htmlElement.onmouseup = (ev) => {
+ isDown = false;
+ ev.preventDefault();
+ }
+
+ htmlElement.onmousemove = (ev: MouseEvent) => {
+ if (isDown) {
+ onPosChange(ev.clientX, ev.clientY);
+ }
+ ev.preventDefault();
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/assets/svg/length-crosshair.svg b/assets/svg/length-crosshair.svg
new file mode 100644
index 0000000000..6db2cf72b3
--- /dev/null
+++ b/assets/svg/length-crosshair.svg
@@ -0,0 +1,73 @@
+
+
diff --git a/test.ts b/test.ts
index 5d077d3544..23da820f35 100644
--- a/test.ts
+++ b/test.ts
@@ -10,6 +10,7 @@ import {Translation} from "./UI/i18n/Translation";
import LocationInput from "./UI/Input/LocationInput";
import Loc from "./Models/Loc";
import {VariableUiElement} from "./UI/Base/VariableUIElement";
+import LengthInput from "./UI/Input/LengthInput";
/*import ValidatedTextField from "./UI/Input/ValidatedTextField";
import Combine from "./UI/Base/Combine";
import {VariableUiElement} from "./UI/Base/VariableUIElement";
@@ -152,13 +153,7 @@ function TestMiniMap() {
}
//*/
-const li = new LocationInput({
- preferCategory:"photo",
- centerLocation:
- new UIEventSource({
- lat: 51.21576, lon: 3.22001, zoom: 19
- })
-})
+const li = new LengthInput()
li.SetStyle("height: 20rem")
.AttachTo("maindiv")