Better compass arrow

This commit is contained in:
Pieter Vander Vennet 2023-12-18 01:30:02 +01:00
parent ba47d1bfad
commit 82409984dc
15 changed files with 219 additions and 114 deletions

View file

@ -1,9 +1,39 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<svg xmlns="http://www.w3.org/2000/svg" width="375px" height="375px" viewBox="0 0 375 375" version="1.1">
<g id="surface1">
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="375px"
height="375px"
viewBox="0 0 375 375"
version="1.1"
id="svg1"
sodipodi:docname="circle.svg"
inkscape:version="1.3.2 (1:1.3.2+202311252150+091e20ef0f)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1" />
<sodipodi:namedview
id="namedview1"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="2.056"
inkscape:cx="187.5"
inkscape:cy="187.5"
inkscape:window-width="1920"
inkscape:window-height="995"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg1" />
<path
style="fill:#000000;"
style="fill:#000000"
class="selectable"
d="M 375 187.5 C 375 291.054688 291.054688 375 187.5 375 C 83.945312 375 0 291.054688 0 187.5 C 0 83.945312 83.945312 0 187.5 0 C 291.054688 0 375 83.945312 375 187.5 Z M 375 187.5 "/>
</g>
d="M 375,187.5 C 375,291.05469 291.05469,375 187.5,375 83.945312,375 0,291.05469 0,187.5 0,83.945312 83.945312,0 187.5,0 291.05469,0 375,83.945312 375,187.5 Z m 0,0"
id="path1" />
</svg>

Before

Width:  |  Height:  |  Size: 486 B

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="375px"
height="375px"
viewBox="0 0 375 375"
version="1.1"
id="svg1"
sodipodi:docname="compass_arrow.svg"
inkscape:version="1.3.2 (1:1.3.2+202311252150+091e20ef0f)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1" />
<sodipodi:namedview
id="namedview1"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="1.0410569"
inkscape:cx="33.139398"
inkscape:cy="182.98711"
inkscape:window-width="1920"
inkscape:window-height="995"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg1" />
<path
id="rect2"
style="fill:#000000;stroke:none;stroke-linecap:round;fill-opacity:1;stroke-opacity:1"
d="M 16.835505,17.477497 79.869453,33.962116 V 80.511444 H 33.96212 Z"
sodipodi:nodetypes="ccccc" />
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -0,0 +1,2 @@
SPDX-FileCopyrightText: Pieter Vander Vennet
SPDX-License-Identifier: CC0

View file

@ -241,6 +241,14 @@
"authors": [],
"sources": []
},
{
"path": "compass_arrow.svg",
"license": "CC0-1.0",
"authors": [
"Pieter Vander Vennet"
],
"sources": []
},
{
"path": "confirm.svg",
"license": "CC0-1.0",

View file

@ -729,14 +729,6 @@ video {
bottom: 0px;
}
.top-1\/2 {
top: 50%;
}
.left-1\/2 {
left: 50%;
}
.right-4 {
right: 1rem;
}
@ -1126,10 +1118,6 @@ video {
height: 0px;
}
.h-5 {
height: 1.25rem;
}
.h-4 {
height: 1rem;
}
@ -1150,6 +1138,10 @@ video {
height: 2.75rem;
}
.h-5 {
height: 1.25rem;
}
.h-48 {
height: 12rem;
}
@ -1240,10 +1232,6 @@ video {
width: 0px;
}
.w-5 {
width: 1.25rem;
}
.w-4 {
width: 1rem;
}
@ -1272,6 +1260,10 @@ video {
width: auto;
}
.w-5 {
width: 1.25rem;
}
.w-10 {
width: 2.5rem;
}
@ -1346,10 +1338,6 @@ video {
cursor: pointer;
}
.cursor-wait {
cursor: wait;
}
.resize {
resize: both;
}
@ -1745,11 +1733,6 @@ video {
background-color: rgb(255 255 255 / var(--tw-bg-opacity));
}
.bg-red-500 {
--tw-bg-opacity: 1;
background-color: rgb(239 68 68 / var(--tw-bg-opacity));
}
.bg-white\/50 {
background-color: rgb(255 255 255 / 0.5);
}
@ -2756,6 +2739,18 @@ a.link-underline {
overflow: visible !important;
}
.compass_arrow {
width: calc( 2.5rem - 1px ) ;
height: calc( 2.5rem - 1px )
}
@media (min-width: 640px) {
.compass_arrow {
width: calc( 2.75rem - 1px ) ;
height: calc( 2.75rem - 1px )
}
}
@-webkit-keyframes glowing-drop-shadow {
from {
-webkit-filter: drop-shadow(5px 5px 60px rgb(128 128 128 / 0.6));
@ -2863,6 +2858,10 @@ a.link-underline {
margin: 0.5rem;
}
.sm\:m-1 {
margin: 0.25rem;
}
.sm\:mx-1 {
margin-left: 0.25rem;
margin-right: 0.25rem;

View file

@ -1,4 +1,4 @@
import { UIEventSource } from "../UIEventSource"
import { Stores, UIEventSource } from "../UIEventSource"
/**
* Exports the device orientation as UIEventSources and detects 'shakes'
@ -33,8 +33,19 @@ export class Orientation {
constructor() {}
public fakeMeasurements() {
public fakeMeasurements(rotateAlpha: boolean = true) {
console.log("Starting fake measurements of orientation sensors", {
alpha: this.alpha,
beta: this.beta,
gamma: this.gamma,
absolute: this.absolute,
})
this.alpha.setData(45)
if (rotateAlpha) {
Stores.Chronic(25).addCallback((date) =>
this.alpha.setData(-(date.getTime() / 10) % 360)
)
}
this.beta.setData(20)
this.gamma.setData(30)
this.absolute.setData(true)

View file

@ -60,6 +60,7 @@ import { Imgur } from "../Logic/ImageProviders/Imgur"
import NearbyFeatureSource from "../Logic/FeatureSource/Sources/NearbyFeatureSource"
import FavouritesFeatureSource from "../Logic/FeatureSource/Sources/FavouritesFeatureSource"
import { ProvidedImage } from "../Logic/ImageProviders/ImageProvider"
import { GeolocationControlState } from "../UI/BigComponents/GeolocationControl"
/**
*
@ -112,6 +113,8 @@ export default class ThemeViewState implements SpecialVisualizationState {
readonly selectedLayer: UIEventSource<LayerConfig>
readonly userRelatedState: UserRelatedState
readonly geolocation: GeoLocationHandler
readonly geolocationControl: GeolocationControlState
readonly lastGeolocationRequestMoment: UIEventSource<Date> = new UIEventSource<Date>(undefined)
readonly imageUploadManager: ImageUploadManager
@ -191,6 +194,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
this.mapProperties,
this.userRelatedState.gpsLocationHistoryRetentionTime
)
this.geolocationControl = new GeolocationControlState(this.geolocation, this.mapProperties)
this.availableLayers = AvailableRasterLayers.layersAvailableAt(this.mapProperties.location)
@ -591,6 +595,13 @@ export default class ThemeViewState implements SpecialVisualizationState {
Translations.t.hotkeyDocumentation.selectAerial,
() => setLayerCategory("photo")
)
Hotkeys.RegisterHotkey(
{ nomod: "L" },
Translations.t.hotkeyDocumentation.geolocate,
() => {
this.geolocationControl.handleClick()
}
)
return true
})
}

View file

@ -12,11 +12,12 @@
export let arialabel: Translation = undefined
</script>
<button
on:click={(e) => dispatch("click", e)}
on:keydown
use:ariaLabel={arialabel}
class={twJoin("pointer-events-auto h-fit w-fit rounded-full", cls)}
class={twJoin("relative pointer-events-auto h-fit w-fit rounded-full", cls)}
>
<slot />
</button>

View file

@ -0,0 +1,44 @@
<script lang="ts">
import Crosshair from "../../assets/svg/Crosshair.svelte"
import Location_refused from "../../assets/svg/Location_refused.svelte"
import { Store } from "../../Logic/UIEventSource.js"
import type { GeolocationPermissionState } from "../../Logic/State/GeoLocationState.js"
import ThemeViewState from "../../Models/ThemeViewState"
import Location_locked from "../../assets/svg/Location_locked.svelte"
import Location_unlocked from "../../assets/svg/Location_unlocked.svelte"
import Location from "../../assets/svg/Location.svelte"
export let state: ThemeViewState
let geolocationstate = state.geolocation.geolocationState
let geopermission: Store<GeolocationPermissionState> = state.geolocation.geolocationState.permission
let allowMoving = geolocationstate.allowMoving
let currentGPSLocation = state.geolocation.geolocationState.currentGPSLocation
let geolocationControlState = state.geolocationControl
let lastClickWasRecent = geolocationControlState.lastClickWithinThreeSecs
</script>
{#if !$allowMoving}
<Location_locked class="h-8 w-8" />
{:else if $currentGPSLocation !== undefined }
<!-- If we have a location; this implies that the location access was granted -->
{#if $lastClickWasRecent}
<Location_unlocked class="h-8 w-8" />
{:else}
<Location class="h-8 w-8" />
{/if}
{:else if $geopermission === "prompt"}
<Location class="h-8 w-8" />
{:else if $geopermission === "requested"}
<!-- Even though disabled, when clicking we request the location again in case the contributor dismissed the location popup -->
<Location
class="h-8 w-8"
style="animation: 3s linear 0s infinite normal none running spin;"
/>
{:else if $geopermission === "denied"}
<Location_refused class="h-8 w-8" />
{:else}
<Location
class="h-8 w-8"
style="animation: 3s linear 0s infinite normal none running spin;"
/>
{/if}

View file

@ -1,9 +1,5 @@
import { VariableUiElement } from "../Base/VariableUIElement"
import Svg from "../../Svg"
import { Store, UIEventSource } from "../../Logic/UIEventSource"
import GeoLocationHandler from "../../Logic/Actors/GeoLocationHandler"
import Hotkeys from "../Base/Hotkeys"
import Translations from "../i18n/Translations"
import Constants from "../../Models/Constants"
import { MapProperties } from "../../Models/MapProperties"
@ -11,11 +7,12 @@ import { MapProperties } from "../../Models/MapProperties"
* Displays an icon depending on the state of the geolocation.
* Will set the 'lock' if clicked twice
*/
export class GeolocationControl extends VariableUiElement {
export class GeolocationControlState {
public readonly lastClick = new UIEventSource<Date>(undefined)
public readonly lastClickWithinThreeSecs: Store<boolean>
private readonly _geolocationHandler: GeoLocationHandler
private readonly _mapProperties: MapProperties
private readonly _lastClickWithinThreeSecs: Store<boolean>
constructor(
geolocationHandler: GeoLocationHandler,
state: MapProperties,
@ -41,60 +38,12 @@ export class GeolocationControl extends VariableUiElement {
return timeDiff <= Constants.zoomToLocationTimeout
}
)
const geolocationState = geolocationHandler?.geolocationState
super(
geolocationState?.permission?.map(
(permission) => {
if (permission === "denied") {
return Svg.location_refused_svg()
}
if (!geolocationState.allowMoving.data) {
return Svg.location_locked_svg()
}
if (geolocationState.currentGPSLocation.data === undefined) {
if (permission === "prompt") {
return Svg.location_empty_svg()
}
// Position not yet found, but permission is either requested or granted: we spin to indicate activity
const icon =
!geolocationHandler.mapHasMoved.data || lastRequestWithinTimeout.data
? Svg.location_svg()
: Svg.location_empty_svg()
return icon
.SetClass("cursor-wait")
.SetStyle("animation: spin 4s linear infinite;")
}
// We have a location, so we show a dot in the center
if (lastClickWithinThreeSecs.data) {
return Svg.location_unlocked_svg()
}
// We have a location, so we show a dot in the center
return Svg.location_svg()
},
[
geolocationState.currentGPSLocation,
geolocationState.allowMoving,
geolocationHandler.mapHasMoved,
lastClickWithinThreeSecs,
lastRequestWithinTimeout,
]
)
)
this._geolocationHandler = geolocationHandler
this._mapProperties = state
this.lastClick = lastClick
this._lastClickWithinThreeSecs = lastClickWithinThreeSecs
this.onClick(() => this.handleClick())
Hotkeys.RegisterHotkey({ nomod: "L" }, Translations.t.hotkeyDocumentation.geolocate, () => {
this.handleClick()
})
this.lastClickWithinThreeSecs = lastClickWithinThreeSecs
lastClick.addCallbackAndRunD((_) => {
window.setTimeout(() => {
@ -148,7 +97,7 @@ export class GeolocationControl extends VariableUiElement {
state.zoom.update((z) => z + 3)
}
if (this._lastClickWithinThreeSecs.data) {
if (this.lastClickWithinThreeSecs.data) {
geolocationState.allowMoving.setData(false)
lastClick.setData(undefined)
return

View file

@ -6,7 +6,6 @@
import MapControlButton from "./Base/MapControlButton.svelte"
import ToSvelte from "./Base/ToSvelte.svelte"
import If from "./Base/If.svelte"
import { GeolocationControl } from "./BigComponents/GeolocationControl"
import type { Feature } from "geojson"
import SelectedElementView from "./BigComponents/SelectedElementView.svelte"
import LayerConfig from "../Models/ThemeConfig/LayerConfig"
@ -65,6 +64,8 @@
import ImageOperations from "./Image/ImageOperations.svelte"
import VisualFeedbackPanel from "./BigComponents/VisualFeedbackPanel.svelte"
import { Orientation } from "../Logic/Web/Orientation"
import GeolocationControl from "./BigComponents/GeolocationControl.svelte"
import Compass_arrow from "../assets/svg/Compass_arrow.svelte"
export let state: ThemeViewState
let layout = state.layout
@ -111,8 +112,6 @@
)
let previewedImage = state.previewedImage
let geolocationControl = new GeolocationControl(state.geolocation, mapproperties, state.lastGeolocationRequestMoment)
function forwardEventToMap(e: KeyboardEvent) {
const mlmap = state.map.data
if (!mlmap) {
@ -262,27 +261,20 @@
<Min class="h-8 w-8" />
</MapControlButton>
<If condition={featureSwitches.featureSwitchGeolocation}>
<div class="relative m-0.5 h-12 w-12 p-0 sm:p-1 md:m-1">
{#if $compassLoaded}
<div class="absolute top-1/2 left-1/2 w-0 h-0">
<div class="w-5 h-5"
style={`rotate: calc(${-$compass}deg + 225deg); transform-origin: 0% 0%; background: var(--button-background);`} />
</div>
{/if}
<div class="absolute top-0 left-0 p-0.5 md:p-1">
<div class="relative m-0.5 md:m-1">
<MapControlButton arialabel={Translations.t.general.labels.jumpToLocation}
cls="m-0 p-0.5 sm:p-1"
on:click={() => geolocationControl.handleClick()}
on:click={() => state.geolocationControl.handleClick()}
on:keydown={forwardEventToMap}
>
<ToSvelte
construct={geolocationControl.SetClass("block w-8 h-8")}
/>
<GeolocationControl {state} /> <!-- h-8 w-8 + p-0.5 sm:p-1 + 2px border => 9 sm: 10 in total-->
</MapControlButton>
{#if $compassLoaded}
<div class="absolute top-0 left-0 w-0 h-0 m-0.5 sm:m-1">
<Compass_arrow class="compass_arrow" style={`rotate: calc(${-$compass}deg + 225deg); transform-origin: 50% 50%;`} />
</div>
{/if}
</div>
</div>
</If>
</div>

View file

@ -1,4 +1,4 @@
<script>
export let color = "#000000"
</script>
<svg {...$$restProps} on:click on:mouseover on:mouseenter on:mouseleave on:keydown xmlns="http://www.w3.org/2000/svg" width="375px" height="375px" viewBox="0 0 375 375" version="1.1"> <g id="surface1"> <path style="fill:{color};" class="selectable" d="M 375 187.5 C 375 291.054688 291.054688 375 187.5 375 C 83.945312 375 0 291.054688 0 187.5 C 0 83.945312 83.945312 0 187.5 0 C 291.054688 0 375 83.945312 375 187.5 Z M 375 187.5 "/> </g> </svg>
<svg {...$$restProps} on:click on:mouseover on:mouseenter on:mouseleave on:keydown width="375px" height="375px" viewBox="0 0 375 375" version="1.1" id="svg1" sodipodi:docname="circle.svg" inkscape:version="1.3.2 (1:1.3.2+202311252150+091e20ef0f)" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg"> <defs id="defs1" /> <sodipodi:namedview id="namedview1" pagecolor="#505050" bordercolor="#eeeeee" borderopacity="1" inkscape:showpageshadow="0" inkscape:pageopacity="0" inkscape:pagecheckerboard="0" inkscape:deskcolor="#d1d1d1" inkscape:zoom="2.056" inkscape:cx="187.5" inkscape:cy="187.5" inkscape:window-width="1920" inkscape:window-height="995" inkscape:window-x="0" inkscape:window-y="0" inkscape:window-maximized="1" inkscape:current-layer="svg1" /> <path style="fill:{color}" class="selectable" d="M 375,187.5 C 375,291.05469 291.05469,375 187.5,375 83.945312,375 0,291.05469 0,187.5 0,83.945312 83.945312,0 187.5,0 291.05469,0 375,83.945312 375,187.5 Z m 0,0" id="path1" /> </svg>

View file

@ -0,0 +1,4 @@
<script>
export let color = "#000000"
</script>
<svg {...$$restProps} on:click on:mouseover on:mouseenter on:mouseleave on:keydown width="375px" height="375px" viewBox="0 0 375 375" version="1.1" id="svg1" sodipodi:docname="compass_arrow.svg" inkscape:version="1.3.2 (1:1.3.2+202311252150+091e20ef0f)" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg"> <defs id="defs1" /> <sodipodi:namedview id="namedview1" pagecolor="#505050" bordercolor="#eeeeee" borderopacity="1" inkscape:showpageshadow="0" inkscape:pageopacity="0" inkscape:pagecheckerboard="0" inkscape:deskcolor="#d1d1d1" inkscape:zoom="1.0410569" inkscape:cx="33.139398" inkscape:cy="182.98711" inkscape:window-width="1920" inkscape:window-height="995" inkscape:window-x="0" inkscape:window-y="0" inkscape:window-maximized="1" inkscape:current-layer="svg1" /> <path id="rect2" style="fill:{color};stroke:none;stroke-linecap:round;fill-opacity:1;stroke-opacity:1" d="M 16.835505,17.477497 79.869453,33.962116 V 80.511444 H 33.96212 Z" sodipodi:nodetypes="ccccc" /> </svg>

View file

@ -590,6 +590,21 @@ a.link-underline {
overflow: visible !important;
}
.compass_arrow {
width: calc( 2.5rem - 1px ) ;
height: calc( 2.5rem - 1px )
}
@media (min-width: 640px) {
.compass_arrow {
width: calc( 2.75rem - 1px ) ;
height: calc( 2.75rem - 1px )
}
}
@-webkit-keyframes glowing-drop-shadow {
from {
filter: drop-shadow(5px 5px 60px rgb(128 128 128 / 0.6));

View file

@ -4,7 +4,7 @@
<head>
<meta charset="UTF-8">
<!-- We disable 'user-scalable'. As we are working with a map, the user might zoom in using a popup, close the popup and _not_ be able to zoom out again. -->
<meta content="width=device-width, initial-scale=1.0, user-scalable=no" name="viewport">
<meta content="width=device-width, initial-scale=1.0, user-scalable=yes" name="viewport">
<!-- CSP -->
<link href="./css/mobile.css" rel="stylesheet"/>
<link href="./css/openinghourstable.css" rel="stylesheet"/>