Feature: show geocoded images on the map when hovered, show interactive minimap on nearbyImages element
This commit is contained in:
parent
d079ba91aa
commit
f3fdc95bd0
23 changed files with 404 additions and 182 deletions
70
assets/layers/geocoded_image/geocoded_image.json
Normal file
70
assets/layers/geocoded_image/geocoded_image.json
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
{
|
||||||
|
"id": "geocoded_image",
|
||||||
|
"source": "special",
|
||||||
|
"name": null,
|
||||||
|
"tagRenderings": [],
|
||||||
|
"pointRendering": [
|
||||||
|
{
|
||||||
|
"location": [
|
||||||
|
"point",
|
||||||
|
"centroid"
|
||||||
|
],
|
||||||
|
"marker": [
|
||||||
|
{
|
||||||
|
"icon": "direction_gradient",
|
||||||
|
"color": {
|
||||||
|
"render": "#44cc22",
|
||||||
|
"mappings": [
|
||||||
|
{
|
||||||
|
"if": "selected=yes",
|
||||||
|
"then": "#cccc22"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"rotation": "{rotation}deg",
|
||||||
|
"rotationAlignment": "map",
|
||||||
|
"pitchAlignment": "map",
|
||||||
|
"iconSize": "60,60"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"location": [
|
||||||
|
"point",
|
||||||
|
"centroid"
|
||||||
|
],
|
||||||
|
"rotationAlignment": "map",
|
||||||
|
"pitchAlignment": "map",
|
||||||
|
"marker": [
|
||||||
|
{
|
||||||
|
"icon": "circle",
|
||||||
|
"color": {
|
||||||
|
"render": "#44cc22",
|
||||||
|
"mappings": [
|
||||||
|
{
|
||||||
|
"if": "selected=yes",
|
||||||
|
"then": "#cccc22"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"iconSize": "14,14"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"location": [
|
||||||
|
"point",
|
||||||
|
"centroid"
|
||||||
|
],
|
||||||
|
"rotationAlignment": "map",
|
||||||
|
"pitchAlignment": "map",
|
||||||
|
"marker": [
|
||||||
|
{
|
||||||
|
"icon": "ring",
|
||||||
|
"color": "#000"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"iconSize": "14,14"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -32,7 +32,7 @@
|
||||||
inkscape:window-maximized="1"
|
inkscape:window-maximized="1"
|
||||||
inkscape:current-layer="svg1" />
|
inkscape:current-layer="svg1" />
|
||||||
<path
|
<path
|
||||||
style="fill:#000000"
|
style="fill:#000000;"
|
||||||
class="selectable"
|
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"
|
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" />
|
id="path1" />
|
||||||
|
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
@ -1,101 +1,55 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
<svg
|
<svg
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
version="1.0"
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
width="860.50732pt"
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
height="860.50732pt"
|
||||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
viewBox="0 0 860.50732 860.50732"
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
preserveAspectRatio="xMidYMid meet"
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
id="svg14"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
version="1.0"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
width="860.50732pt"
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
height="860.50732pt"
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
viewBox="0 0 860.50732 860.50732"
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
preserveAspectRatio="xMidYMid meet"
|
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||||
id="svg14"
|
<defs
|
||||||
sodipodi:docname="direction_gradient.svg"
|
id="defs18">
|
||||||
inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)">
|
<linearGradient
|
||||||
<defs
|
id="linearGradient832">
|
||||||
id="defs18">
|
<stop
|
||||||
<linearGradient
|
style="stop-color:#000000;stop-opacity:1;"
|
||||||
inkscape:collect="always"
|
offset="0"
|
||||||
id="linearGradient832">
|
id="stop828" />
|
||||||
<stop
|
<stop
|
||||||
style="stop-color:#000000;stop-opacity:1;"
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
offset="0"
|
offset="1"
|
||||||
id="stop828"/>
|
id="stop830" />
|
||||||
<stop
|
</linearGradient>
|
||||||
style="stop-color:#000000;stop-opacity:0;"
|
<radialGradient
|
||||||
offset="1"
|
xlink:href="#linearGradient832"
|
||||||
id="stop830"/>
|
id="radialGradient838"
|
||||||
</linearGradient>
|
cx="430.25363"
|
||||||
<radialGradient
|
cy="519.61188"
|
||||||
inkscape:collect="always"
|
fx="430.25363"
|
||||||
xlink:href="#linearGradient832"
|
fy="519.61188"
|
||||||
id="radialGradient838"
|
r="305.54589"
|
||||||
cx="430.25363"
|
gradientTransform="matrix(0.95288409,-0.94890664,0.94542304,0.94938587,-470.98122,345.21193)"
|
||||||
cy="519.61188"
|
gradientUnits="userSpaceOnUse" />
|
||||||
fx="430.25363"
|
</defs>
|
||||||
fy="519.61188"
|
<metadata
|
||||||
r="305.54589"
|
id="metadata2">
|
||||||
gradientTransform="matrix(0.95288409,-0.94890664,0.94542304,0.94938587,-470.98122,345.21193)"
|
|
||||||
gradientUnits="userSpaceOnUse"/>
|
|
||||||
</defs>
|
|
||||||
<sodipodi:namedview
|
|
||||||
pagecolor="#ffffff"
|
|
||||||
bordercolor="#666666"
|
|
||||||
borderopacity="1"
|
|
||||||
objecttolerance="10"
|
|
||||||
gridtolerance="10"
|
|
||||||
guidetolerance="10"
|
|
||||||
inkscape:pageopacity="0"
|
|
||||||
inkscape:pageshadow="2"
|
|
||||||
inkscape:window-width="1920"
|
|
||||||
inkscape:window-height="999"
|
|
||||||
id="namedview16"
|
|
||||||
showgrid="false"
|
|
||||||
showguides="true"
|
|
||||||
inkscape:guide-bbox="true"
|
|
||||||
inkscape:zoom="0.70710678"
|
|
||||||
inkscape:cx="279.00239"
|
|
||||||
inkscape:cy="856.75313"
|
|
||||||
inkscape:window-x="0"
|
|
||||||
inkscape:window-y="0"
|
|
||||||
inkscape:window-maximized="1"
|
|
||||||
inkscape:current-layer="svg14">
|
|
||||||
<sodipodi:guide
|
|
||||||
position="430.25363,862.49682"
|
|
||||||
orientation="1,0"
|
|
||||||
id="guide832"
|
|
||||||
inkscape:locked="false"/>
|
|
||||||
<sodipodi:guide
|
|
||||||
position="-42.427977,430.25368"
|
|
||||||
orientation="0,1"
|
|
||||||
id="guide834"
|
|
||||||
inkscape:locked="false"/>
|
|
||||||
<sodipodi:guide
|
|
||||||
position="398.27788,720.18823"
|
|
||||||
orientation="0,1"
|
|
||||||
id="guide840"
|
|
||||||
inkscape:locked="false"/>
|
|
||||||
</sodipodi:namedview>
|
|
||||||
<metadata
|
|
||||||
id="metadata2">
|
|
||||||
Created by potrace 1.15, written by Peter Selinger 2001-2017
|
Created by potrace 1.15, written by Peter Selinger 2001-2017
|
||||||
<rdf:RDF>
|
<rdf:RDF>
|
||||||
<cc:Work
|
<cc:Work
|
||||||
rdf:about="">
|
rdf:about="">
|
||||||
<dc:format>image/svg+xml</dc:format>
|
<dc:format>image/svg+xml</dc:format>
|
||||||
<dc:type
|
<dc:type
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
<dc:title/>
|
</cc:Work>
|
||||||
</cc:Work>
|
</rdf:RDF>
|
||||||
</rdf:RDF>
|
</metadata>
|
||||||
</metadata>
|
<path
|
||||||
<path
|
style="fill:url(#radialGradient838);fill-opacity:1;stroke:none;stroke-width:2.83464575;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
style="fill:url(#radialGradient838);fill-opacity:1;stroke:none;stroke-width:2.83464575;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
d="M 735.79979,124.70799 C 654.79116,43.598883 544.88842,-2.0206645 430.25389,-2.121103 315.61937,-2.0206592 205.71663,43.598888 124.70801,124.70799 l 305.54588,305.54589 z"
|
||||||
d="M 735.79979,124.70799 C 654.79116,43.598883 544.88842,-2.0206645 430.25389,-2.121103 315.61937,-2.0206592 205.71663,43.598888 124.70801,124.70799 l 305.54588,305.54589 z"
|
id="path836" />
|
||||||
id="path836"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
sodipodi:nodetypes="ccccc"/>
|
|
||||||
</svg>
|
</svg>
|
||||||
|
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 1.8 KiB |
|
@ -234,7 +234,7 @@
|
||||||
{
|
{
|
||||||
"id": "nearby_images",
|
"id": "nearby_images",
|
||||||
"render": {
|
"render": {
|
||||||
"*": "{nearby_images(open,readonly)}"
|
"*": "{nearby_images(,readonly)}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "mapcomplete",
|
"name": "mapcomplete",
|
||||||
"version": "0.46.4",
|
"version": "0.46.5",
|
||||||
"repository": "https://github.com/pietervdvn/MapComplete",
|
"repository": "https://github.com/pietervdvn/MapComplete",
|
||||||
"description": "A small website to edit OSM easily",
|
"description": "A small website to edit OSM easily",
|
||||||
"bugs": "https://github.com/pietervdvn/MapComplete/issues",
|
"bugs": "https://github.com/pietervdvn/MapComplete/issues",
|
||||||
|
@ -91,7 +91,7 @@
|
||||||
"generate:contributor-list": "vite-node scripts/generateContributors.ts",
|
"generate:contributor-list": "vite-node scripts/generateContributors.ts",
|
||||||
"generate:service-worker": "tsc src/service-worker.ts --outFile public/service-worker.js && git_hash=$(git rev-parse HEAD) && sed -i.bak \"s/GITHUB-COMMIT/$git_hash/\" public/service-worker.js && rm public/service-worker.js.bak",
|
"generate:service-worker": "tsc src/service-worker.ts --outFile public/service-worker.js && git_hash=$(git rev-parse HEAD) && sed -i.bak \"s/GITHUB-COMMIT/$git_hash/\" public/service-worker.js && rm public/service-worker.js.bak",
|
||||||
"reset:layeroverview": "npm run prep:layeroverview && npm run generate:layeroverview && npm run refresh:layeroverview",
|
"reset:layeroverview": "npm run prep:layeroverview && npm run generate:layeroverview && npm run refresh:layeroverview",
|
||||||
"prep:layeroverview": "mkdir -p ./src/assets/generated/layers; echo {\\\"themes\\\":[]} > ./src/assets/generated/known_themes.json && echo {\\\"layers\\\": []} > ./src/assets/generated/known_layers.json && rm -f ./src/assets/generated/layers/*.json && rm -f ./src/assets/generated/themes/*.json && cp ./assets/layers/usersettings/usersettings.json ./src/assets/generated/layers/usersettings.json && echo '{}' > ./src/assets/generated/layers/favourite.json && echo '{}' > ./src/assets/generated/layers/summary.json && echo '{}' > ./src/assets/generated/layers/last_click.json && echo '[]' > ./src/assets/generated/theme_overview.json",
|
"prep:layeroverview": "mkdir -p ./src/assets/generated/layers; echo {\\\"themes\\\":[]} > ./src/assets/generated/known_themes.json && echo {\\\"layers\\\": []} > ./src/assets/generated/known_layers.json && rm -f ./src/assets/generated/layers/*.json && rm -f ./src/assets/generated/themes/*.json && cp ./assets/layers/usersettings/usersettings.json ./src/assets/generated/layers/usersettings.json && echo '{}' > ./src/assets/generated/layers/favourite.json && echo '{}' > ./src/assets/generated/layers/summary.json && echo '{}' > ./src/assets/generated/layers/last_click.json && echo '[]' > ./src/assets/generated/theme_overview.json echo '{}' > ./src/assets/generated/geocoded_image.json",
|
||||||
"generate": "npm run generate:licenses && npm run generate:images && npm run generate:charging-stations && npm run generate:translations && npm run refresh:layeroverview && npm run generate:service-worker",
|
"generate": "npm run generate:licenses && npm run generate:images && npm run generate:charging-stations && npm run generate:translations && npm run refresh:layeroverview && npm run generate:service-worker",
|
||||||
"generate:charging-stations": "cd ./assets/layers/charging_station && vite-node csvToJson.ts && cd -",
|
"generate:charging-stations": "cd ./assets/layers/charging_station && vite-node csvToJson.ts && cd -",
|
||||||
"clean:tests": "find . -type f -name \"*.doctest.ts\" | xargs -r rm",
|
"clean:tests": "find . -type f -name \"*.doctest.ts\" | xargs -r rm",
|
||||||
|
|
|
@ -1761,14 +1761,14 @@ input[type="range"].range-lg::-moz-range-thumb {
|
||||||
height: 3.5rem;
|
height: 3.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.h-16 {
|
|
||||||
height: 4rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.h-48 {
|
.h-48 {
|
||||||
height: 12rem;
|
height: 12rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.h-16 {
|
||||||
|
height: 4rem;
|
||||||
|
}
|
||||||
|
|
||||||
.h-40 {
|
.h-40 {
|
||||||
height: 10rem;
|
height: 10rem;
|
||||||
}
|
}
|
||||||
|
@ -3251,11 +3251,6 @@ input[type="range"].range-lg::-moz-range-thumb {
|
||||||
background-color: rgb(0 0 0 / var(--tw-bg-opacity));
|
background-color: rgb(0 0 0 / var(--tw-bg-opacity));
|
||||||
}
|
}
|
||||||
|
|
||||||
.bg-indigo-100 {
|
|
||||||
--tw-bg-opacity: 1;
|
|
||||||
background-color: rgb(229 237 255 / var(--tw-bg-opacity));
|
|
||||||
}
|
|
||||||
|
|
||||||
.bg-gray-100 {
|
.bg-gray-100 {
|
||||||
--tw-bg-opacity: 1;
|
--tw-bg-opacity: 1;
|
||||||
background-color: rgb(243 244 246 / var(--tw-bg-opacity));
|
background-color: rgb(243 244 246 / var(--tw-bg-opacity));
|
||||||
|
@ -3286,6 +3281,11 @@ input[type="range"].range-lg::-moz-range-thumb {
|
||||||
background-color: rgb(253 246 178 / var(--tw-bg-opacity));
|
background-color: rgb(253 246 178 / var(--tw-bg-opacity));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.bg-indigo-100 {
|
||||||
|
--tw-bg-opacity: 1;
|
||||||
|
background-color: rgb(229 237 255 / var(--tw-bg-opacity));
|
||||||
|
}
|
||||||
|
|
||||||
.bg-purple-100 {
|
.bg-purple-100 {
|
||||||
--tw-bg-opacity: 1;
|
--tw-bg-opacity: 1;
|
||||||
background-color: rgb(237 235 254 / var(--tw-bg-opacity));
|
background-color: rgb(237 235 254 / var(--tw-bg-opacity));
|
||||||
|
@ -4032,6 +4032,10 @@ input[type="range"].range-lg::-moz-range-thumb {
|
||||||
padding-right: 1rem;
|
padding-right: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.pt-2 {
|
||||||
|
padding-top: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
.pb-1\.5 {
|
.pb-1\.5 {
|
||||||
padding-bottom: 0.375rem;
|
padding-bottom: 0.375rem;
|
||||||
}
|
}
|
||||||
|
@ -5922,11 +5926,6 @@ svg.apply-fill path {
|
||||||
border-color: rgb(209 213 219 / var(--tw-border-opacity));
|
border-color: rgb(209 213 219 / var(--tw-border-opacity));
|
||||||
}
|
}
|
||||||
|
|
||||||
.hover\:bg-indigo-200:hover {
|
|
||||||
--tw-bg-opacity: 1;
|
|
||||||
background-color: rgb(205 219 254 / var(--tw-bg-opacity));
|
|
||||||
}
|
|
||||||
|
|
||||||
.hover\:bg-gray-100:hover {
|
.hover\:bg-gray-100:hover {
|
||||||
--tw-bg-opacity: 1;
|
--tw-bg-opacity: 1;
|
||||||
background-color: rgb(243 244 246 / var(--tw-bg-opacity));
|
background-color: rgb(243 244 246 / var(--tw-bg-opacity));
|
||||||
|
@ -5962,6 +5961,11 @@ svg.apply-fill path {
|
||||||
background-color: rgb(252 233 106 / var(--tw-bg-opacity));
|
background-color: rgb(252 233 106 / var(--tw-bg-opacity));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hover\:bg-indigo-200:hover {
|
||||||
|
--tw-bg-opacity: 1;
|
||||||
|
background-color: rgb(205 219 254 / var(--tw-bg-opacity));
|
||||||
|
}
|
||||||
|
|
||||||
.hover\:bg-purple-200:hover {
|
.hover\:bg-purple-200:hover {
|
||||||
--tw-bg-opacity: 1;
|
--tw-bg-opacity: 1;
|
||||||
background-color: rgb(220 215 254 / var(--tw-bg-opacity));
|
background-color: rgb(220 215 254 / var(--tw-bg-opacity));
|
||||||
|
@ -8110,6 +8114,10 @@ svg.apply-fill path {
|
||||||
height: 2.75rem;
|
height: 2.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sm\:h-32 {
|
||||||
|
height: 8rem;
|
||||||
|
}
|
||||||
|
|
||||||
.sm\:h-64 {
|
.sm\:h-64 {
|
||||||
height: 16rem;
|
height: 16rem;
|
||||||
}
|
}
|
||||||
|
@ -8299,6 +8307,10 @@ svg.apply-fill path {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.md\:h-64 {
|
||||||
|
height: 16rem;
|
||||||
|
}
|
||||||
|
|
||||||
.md\:h-auto {
|
.md\:h-auto {
|
||||||
height: auto;
|
height: auto;
|
||||||
}
|
}
|
||||||
|
@ -8482,6 +8494,11 @@ svg.apply-fill path {
|
||||||
padding-bottom: 2rem;
|
padding-bottom: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.md\:px-2 {
|
||||||
|
padding-left: 0.5rem;
|
||||||
|
padding-right: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
.md\:pr-2 {
|
.md\:pr-2 {
|
||||||
padding-right: 0.5rem;
|
padding-right: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,14 @@ export interface ProvidedImage {
|
||||||
key: string
|
key: string
|
||||||
provider: ImageProvider
|
provider: ImageProvider
|
||||||
id: string
|
id: string
|
||||||
date?: Date
|
date?: Date,
|
||||||
|
/**
|
||||||
|
* Compass angle of the taken image
|
||||||
|
* 0 = north, 90° = East
|
||||||
|
*/
|
||||||
|
rotation?: number
|
||||||
|
lat?: number,
|
||||||
|
lon?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export default abstract class ImageProvider {
|
export default abstract class ImageProvider {
|
||||||
|
|
|
@ -162,12 +162,14 @@ export class Mapillary extends ImageProvider {
|
||||||
const metadataUrl =
|
const metadataUrl =
|
||||||
"https://graph.mapillary.com/" +
|
"https://graph.mapillary.com/" +
|
||||||
mapillaryId +
|
mapillaryId +
|
||||||
"?fields=thumb_1024_url,thumb_original_url,captured_at,creator&access_token=" +
|
"?fields=thumb_1024_url,thumb_original_url,captured_at,compass_angle,geometry,creator&access_token=" +
|
||||||
Constants.mapillary_client_token_v4
|
Constants.mapillary_client_token_v4
|
||||||
const response = await Utils.downloadJsonCached(metadataUrl, 60 * 60)
|
const response = await Utils.downloadJsonCached(metadataUrl, 60 * 60)
|
||||||
const url = <string>response["thumb_1024_url"]
|
const url = <string>response["thumb_1024_url"]
|
||||||
const url_hd = <string>response["thumb_original_url"]
|
const url_hd = <string>response["thumb_original_url"]
|
||||||
const date = new Date()
|
const date = new Date()
|
||||||
|
const rotation = (720 - Number(response["compass_angle"])) % 360
|
||||||
|
const geometry = response["geometry"]
|
||||||
date.setTime(response["captured_at"])
|
date.setTime(response["captured_at"])
|
||||||
return <ProvidedImage>{
|
return <ProvidedImage>{
|
||||||
id: "" + mapillaryId,
|
id: "" + mapillaryId,
|
||||||
|
@ -176,6 +178,9 @@ export class Mapillary extends ImageProvider {
|
||||||
provider: this,
|
provider: this,
|
||||||
date,
|
date,
|
||||||
key,
|
key,
|
||||||
|
rotation,
|
||||||
|
lat: geometry.coordinates[1],
|
||||||
|
lon: geometry.coordinates[0]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,7 +56,7 @@ export interface P4CPicture {
|
||||||
author?
|
author?
|
||||||
license?
|
license?
|
||||||
detailsUrl?: string
|
detailsUrl?: string
|
||||||
direction?
|
direction?: number,
|
||||||
osmTags?: object /*To copy straight into OSM!*/
|
osmTags?: object /*To copy straight into OSM!*/
|
||||||
thumbUrl: string
|
thumbUrl: string
|
||||||
details: {
|
details: {
|
||||||
|
|
|
@ -25,6 +25,7 @@ export default class Constants {
|
||||||
"last_click",
|
"last_click",
|
||||||
"favourite",
|
"favourite",
|
||||||
"summary",
|
"summary",
|
||||||
|
"geocoded_image"
|
||||||
] as const
|
] as const
|
||||||
/**
|
/**
|
||||||
* Special layers which are not included in a theme by default
|
* Special layers which are not included in a theme by default
|
||||||
|
|
|
@ -718,7 +718,7 @@ export class ValidatePointRendering extends DesugaringStep<PointRenderingConfigJ
|
||||||
if (json.marker && !Array.isArray(json.marker)) {
|
if (json.marker && !Array.isArray(json.marker)) {
|
||||||
context.enter("marker").err("The marker in a pointRendering should be an array")
|
context.enter("marker").err("The marker in a pointRendering should be an array")
|
||||||
}
|
}
|
||||||
if (json.location.length == 0) {
|
if (!(json.location?.length > 0)) {
|
||||||
context
|
context
|
||||||
.enter("location")
|
.enter("location")
|
||||||
.err(
|
.err(
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { Store, UIEventSource } from "../Logic/UIEventSource"
|
||||||
import {
|
import {
|
||||||
FeatureSource,
|
FeatureSource,
|
||||||
IndexedFeatureSource,
|
IndexedFeatureSource,
|
||||||
WritableFeatureSource,
|
WritableFeatureSource
|
||||||
} from "../Logic/FeatureSource/FeatureSource"
|
} from "../Logic/FeatureSource/FeatureSource"
|
||||||
import { OsmConnection } from "../Logic/Osm/OsmConnection"
|
import { OsmConnection } from "../Logic/Osm/OsmConnection"
|
||||||
import { ExportableMap, MapProperties } from "./MapProperties"
|
import { ExportableMap, MapProperties } from "./MapProperties"
|
||||||
|
@ -51,7 +51,7 @@ import SaveFeatureSourceToLocalStorage from "../Logic/FeatureSource/Actors/SaveF
|
||||||
import BBoxFeatureSource from "../Logic/FeatureSource/Sources/TouchesBboxFeatureSource"
|
import BBoxFeatureSource from "../Logic/FeatureSource/Sources/TouchesBboxFeatureSource"
|
||||||
import ThemeViewStateHashActor from "../Logic/Web/ThemeViewStateHashActor"
|
import ThemeViewStateHashActor from "../Logic/Web/ThemeViewStateHashActor"
|
||||||
import NoElementsInViewDetector, {
|
import NoElementsInViewDetector, {
|
||||||
FeatureViewState,
|
FeatureViewState
|
||||||
} from "../Logic/Actors/NoElementsInViewDetector"
|
} from "../Logic/Actors/NoElementsInViewDetector"
|
||||||
import FilteredLayer from "./FilteredLayer"
|
import FilteredLayer from "./FilteredLayer"
|
||||||
import { PreferredRasterLayerSelector } from "../Logic/Actors/PreferredRasterLayerSelector"
|
import { PreferredRasterLayerSelector } from "../Logic/Actors/PreferredRasterLayerSelector"
|
||||||
|
@ -64,13 +64,12 @@ import { GeolocationControlState } from "../UI/BigComponents/GeolocationControl"
|
||||||
import Zoomcontrol from "../UI/Zoomcontrol"
|
import Zoomcontrol from "../UI/Zoomcontrol"
|
||||||
import {
|
import {
|
||||||
SummaryTileSource,
|
SummaryTileSource,
|
||||||
SummaryTileSourceRewriter,
|
SummaryTileSourceRewriter
|
||||||
} from "../Logic/FeatureSource/TiledFeatureSource/SummaryTileSource"
|
} from "../Logic/FeatureSource/TiledFeatureSource/SummaryTileSource"
|
||||||
import summaryLayer from "../assets/generated/layers/summary.json"
|
import summaryLayer from "../assets/generated/layers/summary.json"
|
||||||
import last_click_layerconfig from "../assets/generated/layers/last_click.json"
|
import last_click_layerconfig from "../assets/generated/layers/last_click.json"
|
||||||
|
|
||||||
import { LayerConfigJson } from "./ThemeConfig/Json/LayerConfigJson"
|
import { LayerConfigJson } from "./ThemeConfig/Json/LayerConfigJson"
|
||||||
import Locale from "../UI/i18n/Locale"
|
|
||||||
import Hash from "../Logic/Web/Hash"
|
import Hash from "../Logic/Web/Hash"
|
||||||
import { GeoOperations } from "../Logic/GeoOperations"
|
import { GeoOperations } from "../Logic/GeoOperations"
|
||||||
import { CombinedFetcher } from "../Logic/Web/NearbyImagesSearch"
|
import { CombinedFetcher } from "../Logic/Web/NearbyImagesSearch"
|
||||||
|
@ -154,6 +153,10 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
public readonly toCacheSavers: ReadonlyMap<string, SaveFeatureSourceToLocalStorage>
|
public readonly toCacheSavers: ReadonlyMap<string, SaveFeatureSourceToLocalStorage>
|
||||||
|
|
||||||
public readonly nearbyImageSearcher: CombinedFetcher
|
public readonly nearbyImageSearcher: CombinedFetcher
|
||||||
|
/**
|
||||||
|
* Geocoded images that should be shown on the main map; probably only the currently hovered image
|
||||||
|
*/
|
||||||
|
public readonly geocodedImages: UIEventSource<Feature[]> = new UIEventSource<Feature[]>([ ])
|
||||||
|
|
||||||
constructor(layout: LayoutConfig, mvtAvailableLayers: Set<string>) {
|
constructor(layout: LayoutConfig, mvtAvailableLayers: Set<string>) {
|
||||||
Utils.initDomPurify()
|
Utils.initDomPurify()
|
||||||
|
@ -178,7 +181,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
"oauth_token",
|
"oauth_token",
|
||||||
undefined,
|
undefined,
|
||||||
"Used to complete the login"
|
"Used to complete the login"
|
||||||
),
|
)
|
||||||
})
|
})
|
||||||
this.userRelatedState = new UserRelatedState(
|
this.userRelatedState = new UserRelatedState(
|
||||||
this.osmConnection,
|
this.osmConnection,
|
||||||
|
@ -257,8 +260,8 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
bbox.asGeoJson({
|
bbox.asGeoJson({
|
||||||
zoom: this.mapProperties.zoom.data,
|
zoom: this.mapProperties.zoom.data,
|
||||||
...this.mapProperties.location.data,
|
...this.mapProperties.location.data,
|
||||||
id: "current_view_" + currentViewIndex,
|
id: "current_view_" + currentViewIndex
|
||||||
}),
|
})
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
@ -275,7 +278,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
featurePropertiesStore: this.featureProperties,
|
featurePropertiesStore: this.featureProperties,
|
||||||
osmConnection: this.osmConnection,
|
osmConnection: this.osmConnection,
|
||||||
historicalUserLocations: this.geolocation.historicalUserLocations,
|
historicalUserLocations: this.geolocation.historicalUserLocations,
|
||||||
featureSwitches: this.featureSwitches,
|
featureSwitches: this.featureSwitches
|
||||||
},
|
},
|
||||||
layout?.isLeftRightSensitive() ?? false,
|
layout?.isLeftRightSensitive() ?? false,
|
||||||
(e, extraMsg) => this.reportError(e, extraMsg)
|
(e, extraMsg) => this.reportError(e, extraMsg)
|
||||||
|
@ -303,7 +306,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
"leftover features, such as",
|
"leftover features, such as",
|
||||||
features[0].properties
|
features[0].properties
|
||||||
)
|
)
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
this.perLayer = perLayer.perLayer
|
this.perLayer = perLayer.perLayer
|
||||||
|
@ -359,7 +362,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
{
|
{
|
||||||
currentZoom: this.mapProperties.zoom,
|
currentZoom: this.mapProperties.zoom,
|
||||||
layerState: this.layerState,
|
layerState: this.layerState,
|
||||||
bounds: this.visualFeedbackViewportBounds,
|
bounds: this.visualFeedbackViewportBounds
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
this.hasDataInView = new NoElementsInViewDetector(this).hasFeatureInView
|
this.hasDataInView = new NoElementsInViewDetector(this).hasFeatureInView
|
||||||
|
@ -453,7 +456,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
doShowLayer,
|
doShowLayer,
|
||||||
metaTags: this.userRelatedState.preferencesAsTags,
|
metaTags: this.userRelatedState.preferencesAsTags,
|
||||||
selectedElement: this.selectedElement,
|
selectedElement: this.selectedElement,
|
||||||
fetchStore: (id) => this.featureProperties.getStore(id),
|
fetchStore: (id) => this.featureProperties.getStore(id)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
return filteringFeatureSource
|
return filteringFeatureSource
|
||||||
|
@ -480,7 +483,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
doShowLayer: flayerGps.isDisplayed,
|
doShowLayer: flayerGps.isDisplayed,
|
||||||
layer: flayerGps.layerDef,
|
layer: flayerGps.layerDef,
|
||||||
metaTags: this.userRelatedState.preferencesAsTags,
|
metaTags: this.userRelatedState.preferencesAsTags,
|
||||||
selectedElement: this.selectedElement,
|
selectedElement: this.selectedElement
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -569,7 +572,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
Hotkeys.RegisterHotkey(
|
Hotkeys.RegisterHotkey(
|
||||||
{
|
{
|
||||||
nomod: " ",
|
nomod: " ",
|
||||||
onUp: true,
|
onUp: true
|
||||||
},
|
},
|
||||||
docs.selectItem,
|
docs.selectItem,
|
||||||
() => {
|
() => {
|
||||||
|
@ -595,7 +598,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
Hotkeys.RegisterHotkey(
|
Hotkeys.RegisterHotkey(
|
||||||
{
|
{
|
||||||
nomod: "" + i,
|
nomod: "" + i,
|
||||||
onUp: true,
|
onUp: true
|
||||||
},
|
},
|
||||||
doc,
|
doc,
|
||||||
() => this.selectClosestAtCenter(i - 1)
|
() => this.selectClosestAtCenter(i - 1)
|
||||||
|
@ -608,7 +611,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
}
|
}
|
||||||
Hotkeys.RegisterHotkey(
|
Hotkeys.RegisterHotkey(
|
||||||
{
|
{
|
||||||
nomod: "b",
|
nomod: "b"
|
||||||
},
|
},
|
||||||
docs.openLayersPanel,
|
docs.openLayersPanel,
|
||||||
() => {
|
() => {
|
||||||
|
@ -619,7 +622,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
)
|
)
|
||||||
Hotkeys.RegisterHotkey(
|
Hotkeys.RegisterHotkey(
|
||||||
{
|
{
|
||||||
nomod: "s",
|
nomod: "s"
|
||||||
},
|
},
|
||||||
Translations.t.hotkeyDocumentation.openFilterPanel,
|
Translations.t.hotkeyDocumentation.openFilterPanel,
|
||||||
() => {
|
() => {
|
||||||
|
@ -697,7 +700,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
|
|
||||||
Hotkeys.RegisterHotkey(
|
Hotkeys.RegisterHotkey(
|
||||||
{
|
{
|
||||||
shift: "T",
|
shift: "T"
|
||||||
},
|
},
|
||||||
Translations.t.hotkeyDocumentation.translationMode,
|
Translations.t.hotkeyDocumentation.translationMode,
|
||||||
() => {
|
() => {
|
||||||
|
@ -734,7 +737,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
this.mapProperties.zoom.map((z) => Math.max(Math.floor(z), 0)),
|
this.mapProperties.zoom.map((z) => Math.max(Math.floor(z), 0)),
|
||||||
this.mapProperties,
|
this.mapProperties,
|
||||||
{
|
{
|
||||||
isActive: this.mapProperties.zoom.map((z) => z < maxzoom),
|
isActive: this.mapProperties.zoom.map((z) => z < maxzoom)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -755,6 +758,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
gps_location: this.geolocation.currentUserLocation,
|
gps_location: this.geolocation.currentUserLocation,
|
||||||
gps_location_history: this.geolocation.historicalUserLocations,
|
gps_location_history: this.geolocation.historicalUserLocations,
|
||||||
gps_track: this.geolocation.historicalUserLocationsTrack,
|
gps_track: this.geolocation.historicalUserLocationsTrack,
|
||||||
|
geocoded_image: new StaticFeatureSource(this.geocodedImages),
|
||||||
selected_element: new StaticFeatureSource(
|
selected_element: new StaticFeatureSource(
|
||||||
this.selectedElement.map((f) => (f === undefined ? empty : [f]))
|
this.selectedElement.map((f) => (f === undefined ? empty : [f]))
|
||||||
),
|
),
|
||||||
|
@ -766,7 +770,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
current_view: this.currentView,
|
current_view: this.currentView,
|
||||||
favourite: this.favourites,
|
favourite: this.favourites,
|
||||||
summary: this.featureSummary,
|
summary: this.featureSummary,
|
||||||
last_click: this.lastClickObject,
|
last_click: this.lastClickObject
|
||||||
}
|
}
|
||||||
|
|
||||||
this.closestFeatures.registerSource(specialLayers.favourite, "favourite")
|
this.closestFeatures.registerSource(specialLayers.favourite, "favourite")
|
||||||
|
@ -821,7 +825,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
doShowLayer: flayer.isDisplayed,
|
doShowLayer: flayer.isDisplayed,
|
||||||
layer: flayer.layerDef,
|
layer: flayer.layerDef,
|
||||||
metaTags: this.userRelatedState.preferencesAsTags,
|
metaTags: this.userRelatedState.preferencesAsTags,
|
||||||
selectedElement: this.selectedElement,
|
selectedElement: this.selectedElement
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
const summaryLayerConfig = new LayerConfig(<LayerConfigJson>summaryLayer, "summaryLayer")
|
const summaryLayerConfig = new LayerConfig(<LayerConfigJson>summaryLayer, "summaryLayer")
|
||||||
|
@ -829,7 +833,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
features: specialLayers.summary,
|
features: specialLayers.summary,
|
||||||
layer: summaryLayerConfig,
|
layer: summaryLayerConfig,
|
||||||
// doShowLayer: this.mapProperties.zoom.map((z) => z < maxzoom),
|
// doShowLayer: this.mapProperties.zoom.map((z) => z < maxzoom),
|
||||||
selectedElement: this.selectedElement,
|
selectedElement: this.selectedElement
|
||||||
})
|
})
|
||||||
|
|
||||||
const lastClickLayerConfig = new LayerConfig(
|
const lastClickLayerConfig = new LayerConfig(
|
||||||
|
@ -840,14 +844,14 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
lastClickLayerConfig.isShown === undefined
|
lastClickLayerConfig.isShown === undefined
|
||||||
? specialLayers.last_click
|
? specialLayers.last_click
|
||||||
: specialLayers.last_click.features.mapD((fs) =>
|
: specialLayers.last_click.features.mapD((fs) =>
|
||||||
fs.filter((f) => {
|
fs.filter((f) => {
|
||||||
const matches = lastClickLayerConfig.isShown.matchesProperties(
|
const matches = lastClickLayerConfig.isShown.matchesProperties(
|
||||||
f.properties
|
f.properties
|
||||||
)
|
)
|
||||||
console.debug("LastClick ", f, "matches", matches)
|
console.debug("LastClick ", f, "matches", matches)
|
||||||
return matches
|
return matches
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
new ShowDataLayer(this.map, {
|
new ShowDataLayer(this.map, {
|
||||||
features: new StaticFeatureSource(lastClickFiltered),
|
features: new StaticFeatureSource(lastClickFiltered),
|
||||||
layer: lastClickLayerConfig,
|
layer: lastClickLayerConfig,
|
||||||
|
@ -858,9 +862,9 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
}
|
}
|
||||||
this.map.data.flyTo({
|
this.map.data.flyTo({
|
||||||
zoom: Constants.minZoomLevelToAddNewPoint,
|
zoom: Constants.minZoomLevelToAddNewPoint,
|
||||||
center: GeoOperations.centerpointCoordinates(feature),
|
center: GeoOperations.centerpointCoordinates(feature)
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -871,6 +875,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
this.selectedElement.addCallback((selected) => {
|
this.selectedElement.addCallback((selected) => {
|
||||||
if (selected === undefined) {
|
if (selected === undefined) {
|
||||||
this.focusOnMap()
|
this.focusOnMap()
|
||||||
|
this.geocodedImages.set([])
|
||||||
} else {
|
} else {
|
||||||
this.lastClickObject.clear()
|
this.lastClickObject.clear()
|
||||||
}
|
}
|
||||||
|
@ -953,8 +958,8 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
userid: this.osmConnection.userDetails.data?.uid,
|
userid: this.osmConnection.userDetails.data?.uid,
|
||||||
pendingChanges: this.changes.pendingChanges.data,
|
pendingChanges: this.changes.pendingChanges.data,
|
||||||
previousChanges: this.changes.allChanges.data,
|
previousChanges: this.changes.allChanges.data,
|
||||||
changeRewrites: Utils.MapToObj(this.changes._changesetHandler._remappings),
|
changeRewrites: Utils.MapToObj(this.changes._changesetHandler._remappings)
|
||||||
}),
|
})
|
||||||
})
|
})
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Could not upload an error report")
|
console.error("Could not upload an error report")
|
||||||
|
|
|
@ -132,6 +132,7 @@
|
||||||
<div class="flex h-32 w-max gap-x-2">
|
<div class="flex h-32 w-max gap-x-2">
|
||||||
{#each $unknownImages as image (image)}
|
{#each $unknownImages as image (image)}
|
||||||
<AttributedImage
|
<AttributedImage
|
||||||
|
{state}
|
||||||
imgClass="h-32 w-max shrink-0"
|
imgClass="h-32 w-max shrink-0"
|
||||||
image={{ url: image }}
|
image={{ url: image }}
|
||||||
previewedImage={state.previewedImage}
|
previewedImage={state.previewedImage}
|
||||||
|
|
|
@ -7,10 +7,12 @@
|
||||||
import { Mapillary } from "../../Logic/ImageProviders/Mapillary"
|
import { Mapillary } from "../../Logic/ImageProviders/Mapillary"
|
||||||
import { UIEventSource } from "../../Logic/UIEventSource"
|
import { UIEventSource } from "../../Logic/UIEventSource"
|
||||||
import { MagnifyingGlassPlusIcon } from "@babeard/svelte-heroicons/outline"
|
import { MagnifyingGlassPlusIcon } from "@babeard/svelte-heroicons/outline"
|
||||||
import { CloseButton, Modal } from "flowbite-svelte"
|
import { CloseButton } from "flowbite-svelte"
|
||||||
import ImageOperations from "./ImageOperations.svelte"
|
import ImageOperations from "./ImageOperations.svelte"
|
||||||
import Popup from "../Base/Popup.svelte"
|
import Popup from "../Base/Popup.svelte"
|
||||||
import { onDestroy } from "svelte"
|
import { onDestroy } from "svelte"
|
||||||
|
import type { SpecialVisualizationState } from "../SpecialVisualization"
|
||||||
|
import type { Feature, Point } from "geojson"
|
||||||
|
|
||||||
export let image: Partial<ProvidedImage>
|
export let image: Partial<ProvidedImage>
|
||||||
let fallbackImage: string = undefined
|
let fallbackImage: string = undefined
|
||||||
|
@ -20,19 +22,43 @@
|
||||||
|
|
||||||
let imgEl: HTMLImageElement
|
let imgEl: HTMLImageElement
|
||||||
export let imgClass: string = undefined
|
export let imgClass: string = undefined
|
||||||
|
export let state: SpecialVisualizationState = undefined
|
||||||
export let attributionFormat: "minimal" | "medium" | "large" = "medium"
|
export let attributionFormat: "minimal" | "medium" | "large" = "medium"
|
||||||
export let previewedImage: UIEventSource<ProvidedImage>
|
export let previewedImage: UIEventSource<ProvidedImage>
|
||||||
export let canZoom = previewedImage !== undefined
|
export let canZoom = previewedImage !== undefined
|
||||||
let loaded = false
|
let loaded = false
|
||||||
let showBigPreview = new UIEventSource(false)
|
let showBigPreview = new UIEventSource(false)
|
||||||
onDestroy(showBigPreview.addCallbackAndRun(shown=>{
|
onDestroy(showBigPreview.addCallbackAndRun(shown => {
|
||||||
if(!shown){
|
if (!shown) {
|
||||||
previewedImage.set(false)
|
previewedImage.set(false)
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
onDestroy(previewedImage.addCallbackAndRun(previewedImage => {
|
onDestroy(previewedImage.addCallbackAndRun(previewedImage => {
|
||||||
showBigPreview.set(previewedImage?.id === image.id)
|
showBigPreview.set(previewedImage?.id === image.id)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
function highlight(entered: boolean = true) {
|
||||||
|
if (!entered) {
|
||||||
|
state?.geocodedImages.set([])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (isNaN(image.lon) || isNaN(image.lat)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const f: Feature<Point> = {
|
||||||
|
type: "Feature",
|
||||||
|
properties: {
|
||||||
|
id: image.id,
|
||||||
|
rotation: image.rotation
|
||||||
|
},
|
||||||
|
geometry: {
|
||||||
|
type: "Point",
|
||||||
|
coordinates: [image.lon, image.lat]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log(f)
|
||||||
|
state?.geocodedImages.set([f])
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Popup shown={showBigPreview} bodyPadding="p-0" dismissable={true}>
|
<Popup shown={showBigPreview} bodyPadding="p-0" dismissable={true}>
|
||||||
|
@ -48,7 +74,10 @@
|
||||||
</div>
|
</div>
|
||||||
</Popup>
|
</Popup>
|
||||||
<div class="relative shrink-0">
|
<div class="relative shrink-0">
|
||||||
<div class="relative w-fit">
|
<div class="relative w-fit"
|
||||||
|
on:mouseenter={() => highlight()}
|
||||||
|
on:mouseleave={() => highlight(false)}
|
||||||
|
>
|
||||||
<img
|
<img
|
||||||
bind:this={imgEl}
|
bind:this={imgEl}
|
||||||
on:load={() => (loaded = true)}
|
on:load={() => (loaded = true)}
|
||||||
|
@ -68,7 +97,7 @@
|
||||||
{#if canZoom && loaded}
|
{#if canZoom && loaded}
|
||||||
<div
|
<div
|
||||||
class="bg-black-transparent absolute right-0 top-0 rounded-bl-full"
|
class="bg-black-transparent absolute right-0 top-0 rounded-bl-full"
|
||||||
on:click={() => previewedImage.set(image)}>
|
on:click={() => previewedImage.set(image)}>
|
||||||
<MagnifyingGlassPlusIcon class="h-8 w-8 cursor-zoom-in pl-3 pb-3" color="white" />
|
<MagnifyingGlassPlusIcon class="h-8 w-8 cursor-zoom-in pl-3 pb-3" color="white" />
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
@ -8,7 +8,6 @@ import ImageProvider, { ProvidedImage } from "../../Logic/ImageProviders/ImagePr
|
||||||
import { OsmConnection } from "../../Logic/Osm/OsmConnection"
|
import { OsmConnection } from "../../Logic/Osm/OsmConnection"
|
||||||
import { Changes } from "../../Logic/Osm/Changes"
|
import { Changes } from "../../Logic/Osm/Changes"
|
||||||
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"
|
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"
|
||||||
import { Feature } from "geojson"
|
|
||||||
import SvelteUIElement from "../Base/SvelteUIElement"
|
import SvelteUIElement from "../Base/SvelteUIElement"
|
||||||
import AttributedImage from "./AttributedImage.svelte"
|
import AttributedImage from "./AttributedImage.svelte"
|
||||||
|
|
||||||
|
@ -30,6 +29,7 @@ export class ImageCarousel extends Toggle {
|
||||||
try {
|
try {
|
||||||
let image: BaseUIElement = new SvelteUIElement(AttributedImage, {
|
let image: BaseUIElement = new SvelteUIElement(AttributedImage, {
|
||||||
image: url,
|
image: url,
|
||||||
|
state,
|
||||||
previewedImage: state?.previewedImage,
|
previewedImage: state?.previewedImage,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -14,8 +14,8 @@
|
||||||
import AttributedImage from "./AttributedImage.svelte"
|
import AttributedImage from "./AttributedImage.svelte"
|
||||||
import SpecialTranslation from "../Popup/TagRendering/SpecialTranslation.svelte"
|
import SpecialTranslation from "../Popup/TagRendering/SpecialTranslation.svelte"
|
||||||
import LoginToggle from "../Base/LoginToggle.svelte"
|
import LoginToggle from "../Base/LoginToggle.svelte"
|
||||||
import ImagePreview from "./ImagePreview.svelte"
|
import { onDestroy } from "svelte"
|
||||||
import FloatOver from "../Base/FloatOver.svelte"
|
import { Utils } from "../../Utils"
|
||||||
|
|
||||||
export let tags: UIEventSource<OsmTags>
|
export let tags: UIEventSource<OsmTags>
|
||||||
export let state: SpecialVisualizationState
|
export let state: SpecialVisualizationState
|
||||||
|
@ -23,6 +23,8 @@
|
||||||
export let feature: Feature
|
export let feature: Feature
|
||||||
export let layer: LayerConfig
|
export let layer: LayerConfig
|
||||||
|
|
||||||
|
export let highlighted: UIEventSource<string> = undefined
|
||||||
|
|
||||||
export let linkable = true
|
export let linkable = true
|
||||||
let targetValue = Object.values(image.osmTags)[0]
|
let targetValue = Object.values(image.osmTags)[0]
|
||||||
let isLinked = new UIEventSource(Object.values(tags.data).some((v) => targetValue === v))
|
let isLinked = new UIEventSource(Object.values(tags.data).some((v) => targetValue === v))
|
||||||
|
@ -33,7 +35,7 @@
|
||||||
key: undefined,
|
key: undefined,
|
||||||
provider: AllImageProviders.byName(image.provider),
|
provider: AllImageProviders.byName(image.provider),
|
||||||
date: new Date(image.date),
|
date: new Date(image.date),
|
||||||
id: Object.values(image.osmTags)[0],
|
id: Object.values(image.osmTags)[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
async function applyLink(isLinked: boolean) {
|
async function applyLink(isLinked: boolean) {
|
||||||
|
@ -44,7 +46,7 @@
|
||||||
if (isLinked) {
|
if (isLinked) {
|
||||||
const action = new LinkImageAction(currentTags.id, key, url, tags, {
|
const action = new LinkImageAction(currentTags.id, key, url, tags, {
|
||||||
theme: tags.data._orig_theme ?? state.layout.id,
|
theme: tags.data._orig_theme ?? state.layout.id,
|
||||||
changeType: "link-image",
|
changeType: "link-image"
|
||||||
})
|
})
|
||||||
await state.changes.applyAction(action)
|
await state.changes.applyAction(action)
|
||||||
} else {
|
} else {
|
||||||
|
@ -53,7 +55,7 @@
|
||||||
if (v === url) {
|
if (v === url) {
|
||||||
const action = new ChangeTagAction(currentTags.id, new Tag(k, ""), currentTags, {
|
const action = new ChangeTagAction(currentTags.id, new Tag(k, ""), currentTags, {
|
||||||
theme: tags.data._orig_theme ?? state.layout.id,
|
theme: tags.data._orig_theme ?? state.layout.id,
|
||||||
changeType: "remove-image",
|
changeType: "remove-image"
|
||||||
})
|
})
|
||||||
state.changes.applyAction(action)
|
state.changes.applyAction(action)
|
||||||
}
|
}
|
||||||
|
@ -62,16 +64,30 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
isLinked.addCallback((isLinked) => applyLink(isLinked))
|
isLinked.addCallback((isLinked) => applyLink(isLinked))
|
||||||
|
|
||||||
|
let element: HTMLDivElement
|
||||||
|
if (highlighted) {
|
||||||
|
|
||||||
|
onDestroy(
|
||||||
|
highlighted.addCallbackD(highlightedUrl => {
|
||||||
|
if (highlightedUrl === image.pictureUrl) {
|
||||||
|
Utils.scrollIntoView(element)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="flex w-fit shrink-0 flex-col overflow-hidden rounded-lg"
|
class="flex w-fit shrink-0 flex-col overflow-hidden rounded-lg"
|
||||||
class:border-interactive={$isLinked}
|
class:border-interactive={$isLinked || $highlighted === image.pictureUrl}
|
||||||
style="border-width: 2px"
|
style="border-width: 2px"
|
||||||
|
bind:this={element}
|
||||||
>
|
>
|
||||||
<AttributedImage
|
<AttributedImage
|
||||||
|
{state}
|
||||||
image={providedImage}
|
image={providedImage}
|
||||||
imgClass="max-h-64 w-auto"
|
imgClass="max-h-64 w-auto sm:h-32 md:h-64"
|
||||||
previewedImage={state.previewedImage}
|
previewedImage={state.previewedImage}
|
||||||
attributionFormat="minimal"
|
attributionFormat="minimal"
|
||||||
>
|
>
|
||||||
|
|
|
@ -7,13 +7,23 @@
|
||||||
import type { SpecialVisualizationState } from "../SpecialVisualization"
|
import type { SpecialVisualizationState } from "../SpecialVisualization"
|
||||||
import type { P4CPicture } from "../../Logic/Web/NearbyImagesSearch"
|
import type { P4CPicture } from "../../Logic/Web/NearbyImagesSearch"
|
||||||
import LinkableImage from "./LinkableImage.svelte"
|
import LinkableImage from "./LinkableImage.svelte"
|
||||||
import type { Feature } from "geojson"
|
import type { Feature, Point } from "geojson"
|
||||||
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
|
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
|
||||||
import Loading from "../Base/Loading.svelte"
|
import Loading from "../Base/Loading.svelte"
|
||||||
import AllImageProviders from "../../Logic/ImageProviders/AllImageProviders"
|
import AllImageProviders from "../../Logic/ImageProviders/AllImageProviders"
|
||||||
import Tr from "../Base/Tr.svelte"
|
import Tr from "../Base/Tr.svelte"
|
||||||
import Translations from "../i18n/Translations"
|
import Translations from "../i18n/Translations"
|
||||||
import MapillaryLink from "../BigComponents/MapillaryLink.svelte"
|
import MapillaryLink from "../BigComponents/MapillaryLink.svelte"
|
||||||
|
import MaplibreMap from "../Map/MaplibreMap.svelte"
|
||||||
|
import { Map as MlMap } from "maplibre-gl"
|
||||||
|
import { MapLibreAdaptor } from "../Map/MapLibreAdaptor"
|
||||||
|
import ShowDataLayer from "../Map/ShowDataLayer"
|
||||||
|
import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeatureSource"
|
||||||
|
import * as geocoded_image from "../../assets/generated/layers/geocoded_image.json"
|
||||||
|
import type { LayerConfigJson } from "../../Models/ThemeConfig/Json/LayerConfigJson"
|
||||||
|
import { onDestroy } from "svelte"
|
||||||
|
import { BBox } from "../../Logic/BBox"
|
||||||
|
|
||||||
|
|
||||||
export let tags: UIEventSource<OsmTags>
|
export let tags: UIEventSource<OsmTags>
|
||||||
export let state: SpecialVisualizationState
|
export let state: SpecialVisualizationState
|
||||||
|
@ -42,12 +52,100 @@
|
||||||
[loadedImages]
|
[loadedImages]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
let asFeatures = result.map(p4cs => p4cs.map(p4c => (<Feature<Point>>{
|
||||||
|
type: "Feature",
|
||||||
|
geometry: {
|
||||||
|
type: "Point",
|
||||||
|
coordinates: [p4c.coordinates.lng, p4c.coordinates.lat]
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
id: p4c.pictureUrl,
|
||||||
|
rotation: p4c.direction
|
||||||
|
}
|
||||||
|
})))
|
||||||
|
|
||||||
|
let selected = new UIEventSource<P4CPicture>(undefined)
|
||||||
|
let selectedAsFeature = selected.mapD(s => {
|
||||||
|
return [<Feature<Point>>{
|
||||||
|
type: "Feature",
|
||||||
|
geometry: {
|
||||||
|
type: "Point",
|
||||||
|
coordinates: [s.coordinates.lng, s.coordinates.lat]
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
id: s.pictureUrl,
|
||||||
|
selected: "yes",
|
||||||
|
rotation: s.direction
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
|
||||||
let someLoading = imageState.state.mapD((stateRecord) =>
|
let someLoading = imageState.state.mapD((stateRecord) =>
|
||||||
Object.values(stateRecord).some((v) => v === "loading")
|
Object.values(stateRecord).some((v) => v === "loading")
|
||||||
)
|
)
|
||||||
let errors = imageState.state.mapD((stateRecord) =>
|
let errors = imageState.state.mapD((stateRecord) =>
|
||||||
Object.keys(stateRecord).filter((k) => stateRecord[k] === "error")
|
Object.keys(stateRecord).filter((k) => stateRecord[k] === "error")
|
||||||
)
|
)
|
||||||
|
let highlighted = new UIEventSource<string>(undefined)
|
||||||
|
|
||||||
|
onDestroy(highlighted.addCallbackD(hl => {
|
||||||
|
const p4c = result.data?.find(i => i.pictureUrl === hl)
|
||||||
|
selected.set(p4c)
|
||||||
|
}
|
||||||
|
))
|
||||||
|
|
||||||
|
let map: UIEventSource<MlMap> = new UIEventSource<MlMap>(undefined)
|
||||||
|
let mapProperties = new MapLibreAdaptor(map, {
|
||||||
|
rasterLayer: state.mapProperties.rasterLayer,
|
||||||
|
rotation: state.mapProperties.rotation,
|
||||||
|
pitch: state.mapProperties.pitch,
|
||||||
|
zoom: new UIEventSource<number>(16),
|
||||||
|
location: new UIEventSource({ lon, lat }),
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
const geocodedImageLayer = new LayerConfig(<LayerConfigJson>geocoded_image)
|
||||||
|
new ShowDataLayer(map, {
|
||||||
|
features: new StaticFeatureSource(asFeatures),
|
||||||
|
layer: geocodedImageLayer,
|
||||||
|
zoomToFeatures: true,
|
||||||
|
onClick: (feature) => {
|
||||||
|
highlighted.set(feature.properties.id)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
ShowDataLayer.showMultipleLayers(
|
||||||
|
map,
|
||||||
|
new StaticFeatureSource([feature]),
|
||||||
|
state.layout.layers
|
||||||
|
)
|
||||||
|
|
||||||
|
onDestroy(
|
||||||
|
asFeatures.addCallbackAndRunD(features => {
|
||||||
|
if(features.length == 0){
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let bbox = BBox.get(features[0])
|
||||||
|
for (const f of features) {
|
||||||
|
bbox = bbox.unionWith(BBox.get(f))
|
||||||
|
}
|
||||||
|
mapProperties.maxbounds.set(bbox.pad(1.1))
|
||||||
|
})
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
new ShowDataLayer(map, {
|
||||||
|
features: new StaticFeatureSource(selectedAsFeature),
|
||||||
|
layer: geocodedImageLayer,
|
||||||
|
onClick: (feature) => {
|
||||||
|
highlighted.set(feature.properties.id)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
|
@ -62,12 +160,24 @@
|
||||||
{:else}
|
{:else}
|
||||||
<div class="flex w-full space-x-4 overflow-x-auto" style="scroll-snap-type: x proximity">
|
<div class="flex w-full space-x-4 overflow-x-auto" style="scroll-snap-type: x proximity">
|
||||||
{#each $result as image (image.pictureUrl)}
|
{#each $result as image (image.pictureUrl)}
|
||||||
<span class="w-fit shrink-0" style="scroll-snap-align: start">
|
<span class="w-fit shrink-0" style="scroll-snap-align: start"
|
||||||
<LinkableImage {tags} {image} {state} {feature} {layer} {linkable} />
|
on:mouseenter={() => {highlighted.set(image.pictureUrl)}}
|
||||||
|
on:mouseleave={() =>{ highlighted.set(undefined); selected.set(undefined)}}
|
||||||
|
>
|
||||||
|
<LinkableImage {tags} {image} {state} {feature} {layer} {linkable} {highlighted} />
|
||||||
</span>
|
</span>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
<span class="self-end pt-2">
|
||||||
|
|
||||||
|
<MapillaryLink
|
||||||
|
large={false}
|
||||||
|
mapProperties={{ zoom: new ImmutableStore(16), location: new ImmutableStore({ lon, lat }) }}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
|
||||||
<div class="my-2 flex justify-between">
|
<div class="my-2 flex justify-between">
|
||||||
<div>
|
<div>
|
||||||
{#if $someLoading && $result.length > 0}
|
{#if $someLoading && $result.length > 0}
|
||||||
|
@ -80,9 +190,10 @@
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
<MapillaryLink
|
</div>
|
||||||
large={false}
|
|
||||||
mapProperties={{ zoom: new ImmutableStore(16), location: new ImmutableStore({ lon, lat }) }}
|
|
||||||
/>
|
<div class="h-48">
|
||||||
|
<MaplibreMap interactive={false} {map} {mapProperties} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -11,8 +11,9 @@
|
||||||
import Camera_plus from "../../assets/svg/Camera_plus.svelte"
|
import Camera_plus from "../../assets/svg/Camera_plus.svelte"
|
||||||
import LoginToggle from "../Base/LoginToggle.svelte"
|
import LoginToggle from "../Base/LoginToggle.svelte"
|
||||||
import { ariaLabel } from "../../Utils/ariaLabel"
|
import { ariaLabel } from "../../Utils/ariaLabel"
|
||||||
import { Accordion, AccordionItem } from "flowbite-svelte"
|
import { Accordion, AccordionItem, Modal } from "flowbite-svelte"
|
||||||
import AccordionSingle from "../Flowbite/AccordionSingle.svelte"
|
import AccordionSingle from "../Flowbite/AccordionSingle.svelte"
|
||||||
|
import Popup from "../Base/Popup.svelte"
|
||||||
|
|
||||||
export let tags: UIEventSource<OsmTags>
|
export let tags: UIEventSource<OsmTags>
|
||||||
export let state: SpecialVisualizationState
|
export let state: SpecialVisualizationState
|
||||||
|
@ -24,15 +25,16 @@
|
||||||
export let layer: LayerConfig
|
export let layer: LayerConfig
|
||||||
const t = Translations.t.image.nearby
|
const t = Translations.t.image.nearby
|
||||||
|
|
||||||
let expanded = false
|
|
||||||
let enableLogin = state.featureSwitches.featureSwitchEnableLogin
|
let enableLogin = state.featureSwitches.featureSwitchEnableLogin
|
||||||
|
export let shown = new UIEventSource(false)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if enableLogin.data}
|
{#if enableLogin.data}
|
||||||
<AccordionSingle>
|
<button on:click={() => {shown.set(!shown.data)}}><Tr t={t.seeNearby}/> </button>
|
||||||
<span slot="header" class="p-2 text-base">
|
<Popup {shown} bodyPadding="p-4">
|
||||||
|
<span slot="header">
|
||||||
<Tr t={t.seeNearby} />
|
<Tr t={t.seeNearby} />
|
||||||
</span>
|
</span>
|
||||||
<NearbyImages {tags} {state} {lon} {lat} {feature} {linkable} {layer} />
|
<NearbyImages {tags} {state} {lon} {lat} {feature} {linkable} {layer} />
|
||||||
</AccordionSingle>
|
</Popup>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
@ -121,7 +121,7 @@
|
||||||
<HeartOutlineIcon style="--svg-color: {color}" class={twMerge(clss, "apply-fill")} />
|
<HeartOutlineIcon style="--svg-color: {color}" class={twMerge(clss, "apply-fill")} />
|
||||||
{:else if icon === "confirm"}
|
{:else if icon === "confirm"}
|
||||||
<Confirm class={clss} {color} />
|
<Confirm class={clss} {color} />
|
||||||
{:else if icon === "direction"}
|
{:else if icon === "direction" || icon === "direction_gradient"}
|
||||||
<Direction_gradient class={clss} {color} />
|
<Direction_gradient class={clss} {color} />
|
||||||
{:else if icon === "not_found"}
|
{:else if icon === "not_found"}
|
||||||
<Not_found class={twMerge(clss, "no-image-background")} {color} />
|
<Not_found class={twMerge(clss, "no-image-background")} {color} />
|
||||||
|
|
|
@ -159,10 +159,9 @@ class PointRenderingLayer {
|
||||||
})
|
})
|
||||||
|
|
||||||
if (this._onClick) {
|
if (this._onClick) {
|
||||||
const self = this
|
el.addEventListener("click", (ev)=> {
|
||||||
el.addEventListener("click", function (ev) {
|
|
||||||
ev.preventDefault()
|
ev.preventDefault()
|
||||||
self._onClick(feature)
|
this._onClick(feature)
|
||||||
// Workaround to signal the MapLibreAdaptor to ignore this click
|
// Workaround to signal the MapLibreAdaptor to ignore this click
|
||||||
ev["consumed"] = true
|
ev["consumed"] = true
|
||||||
})
|
})
|
||||||
|
|
|
@ -93,6 +93,7 @@ export interface SpecialVisualizationState {
|
||||||
readonly previewedImage: UIEventSource<ProvidedImage>
|
readonly previewedImage: UIEventSource<ProvidedImage>
|
||||||
readonly nearbyImageSearcher: CombinedFetcher
|
readonly nearbyImageSearcher: CombinedFetcher
|
||||||
readonly geolocation: GeoLocationHandler
|
readonly geolocation: GeoLocationHandler
|
||||||
|
readonly geocodedImages : UIEventSource<Feature[]>
|
||||||
|
|
||||||
showCurrentLocationOn(map: Store<MlMap>): ShowDataLayer
|
showCurrentLocationOn(map: Store<MlMap>): ShowDataLayer
|
||||||
reportError(message: string): Promise<void>
|
reportError(message: string): Promise<void>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<script>
|
<script>
|
||||||
export let color = "#000000"
|
export let color = "#000000"
|
||||||
</script>
|
</script>
|
||||||
<svg {...$$restProps} on:click on:mouseover on:mouseenter on:mouseleave on:keydown on:focus 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:xlink="http://www.w3.org/1999/xlink" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns="http://www.w3.org/2000/svg" version="1.0" width="860.50732pt" height="860.50732pt" viewBox="0 0 860.50732 860.50732" preserveAspectRatio="xMidYMid meet" id="svg14" sodipodi:docname="direction_gradient.svg" inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)"> <defs id="defs18"> <linearGradient inkscape:collect="always" id="linearGradient832"> <stop style="stop-color:{color};stop-opacity:1;" offset="0" id="stop828"/> <stop style="stop-color:{color};stop-opacity:0;" offset="1" id="stop830"/> </linearGradient> <radialGradient inkscape:collect="always" xlink:href="#linearGradient832" id="radialGradient838" cx="430.25363" cy="519.61188" fx="430.25363" fy="519.61188" r="305.54589" gradientTransform="matrix(0.95288409,-0.94890664,0.94542304,0.94938587,-470.98122,345.21193)" gradientUnits="userSpaceOnUse"/> </defs> <sodipodi:namedview pagecolor="#ffffff" bordercolor="#666666" borderopacity="1" objecttolerance="10" gridtolerance="10" guidetolerance="10" inkscape:pageopacity="0" inkscape:pageshadow="2" inkscape:window-width="1920" inkscape:window-height="999" id="namedview16" showgrid="false" showguides="true" inkscape:guide-bbox="true" inkscape:zoom="0.70710678" inkscape:cx="279.00239" inkscape:cy="856.75313" inkscape:window-x="0" inkscape:window-y="0" inkscape:window-maximized="1" inkscape:current-layer="svg14"> <sodipodi:guide position="430.25363,862.49682" orientation="1,0" id="guide832" inkscape:locked="false"/> <sodipodi:guide position="-42.427977,430.25368" orientation="0,1" id="guide834" inkscape:locked="false"/> <sodipodi:guide position="398.27788,720.18823" orientation="0,1" id="guide840" inkscape:locked="false"/> </sodipodi:namedview> <metadata id="metadata2"> Created by potrace 1.15, written by Peter Selinger 2001-2017 <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> <path style="fill:url(#radialGradient838);fill-opacity:1;stroke:none;stroke-width:2.83464575;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="M 735.79979,124.70799 C 654.79116,43.598883 544.88842,-2.0206645 430.25389,-2.121103 315.61937,-2.0206592 205.71663,43.598888 124.70801,124.70799 l 305.54588,305.54589 z" id="path836" inkscape:connector-curvature="0" sodipodi:nodetypes="ccccc"/> </svg>
|
<svg {...$$restProps} on:click on:mouseover on:mouseenter on:mouseleave on:keydown on:focus version="1.0" width="860.50732pt" height="860.50732pt" viewBox="0 0 860.50732 860.50732" preserveAspectRatio="xMidYMid meet" id="svg14" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/"> <defs id="defs18"> <linearGradient id="linearGradient832"> <stop style="stop-color:{color};stop-opacity:1;" offset="0" id="stop828" /> <stop style="stop-color:{color};stop-opacity:0;" offset="1" id="stop830" /> </linearGradient> <radialGradient xlink:href="#linearGradient832" id="radialGradient838" cx="430.25363" cy="519.61188" fx="430.25363" fy="519.61188" r="305.54589" gradientTransform="matrix(0.95288409,-0.94890664,0.94542304,0.94938587,-470.98122,345.21193)" gradientUnits="userSpaceOnUse" /> </defs> <metadata id="metadata2"> Created by potrace 1.15, written by Peter Selinger 2001-2017 <rdf:RDF> <cc:Work rdf:about=""> <dc:format>image/svg+xml</dc:format> <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> </cc:Work> </rdf:RDF> </metadata> <path style="fill:url(#radialGradient838);fill-opacity:1;stroke:none;stroke-width:2.83464575;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="M 735.79979,124.70799 C 654.79116,43.598883 544.88842,-2.0206645 430.25389,-2.121103 315.61937,-2.0206592 205.71663,43.598888 124.70801,124.70799 l 305.54588,305.54589 z" id="path836" /> </svg>
|
4
src/assets/svg/Unsnap.svelte
Normal file
4
src/assets/svg/Unsnap.svelte
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
<script>
|
||||||
|
export let color = "#000000"
|
||||||
|
</script>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) --> <svg {...$$restProps} on:click on:mouseover on:mouseenter on:mouseleave on:keydown on:focus width="120" height="120" viewBox="0 0 120 120" version="1.1" id="svg1" xml:space="preserve" inkscape:version="1.3.2 (1:1.3.2+202311252150+091e20ef0f)" sodipodi:docname="unsnap.svg" 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"><sodipodi:namedview id="namedview1" pagecolor="#ffffff" bordercolor="#999999" borderopacity="1" inkscape:showpageshadow="2" inkscape:pageopacity="0" inkscape:pagecheckerboard="0" inkscape:deskcolor="#d1d1d1" inkscape:document-units="px" showguides="true" inkscape:zoom="4.5168066" inkscape:cx="51.695815" inkscape:cy="69.186048" inkscape:window-width="1920" inkscape:window-height="995" inkscape:window-x="0" inkscape:window-y="0" inkscape:window-maximized="1" inkscape:current-layer="layer1"><sodipodi:guide position="315.49944,61.936443" orientation="0,-1" id="guide2" inkscape:locked="false" /></sodipodi:namedview><defs id="defs1" /><g inkscape:label="Layer 1" inkscape:groupmode="layer" id="layer1" transform="translate(-5,-5)"><path id="path1-2" style="fill:{color};fill-opacity:1;stroke-width:3.93092" d="m 91.670867,40.074491 c 0.948679,0.909931 1.380066,2.234124 1.148907,3.527969 l -3.447995,17.260743 c -0.381611,2.137177 -2.526217,4.001294 -4.081205,2.486091 L 79.257979,57.469775 64.262133,72.31877 c -1.383257,1.369713 -3.807955,0.909932 -4.298111,0.288099 -1.707154,-2.165786 -0.138139,-3.968458 0.177549,-4.304093 L 74.600311,52.930303 68.567707,47.05079 c -1.554665,-1.51554 0.253754,-3.707343 2.380393,-4.143751 l 17.16644,-3.89041 c 1.287478,-0.264342 2.622313,0.132882 3.556328,1.057866 z" sodipodi:nodetypes="cccccsssccccc" /><g id="g8" transform="matrix(-1,0,0,1,132.686,0)" /><g id="g10" transform="matrix(0.99755231,-0.06992414,-0.06992414,-0.99755231,14.642674,124.44485)"><path style="color:{color};fill:{color};stroke-linecap:round;-inkscape-stroke:none" d="M 10,90 45,10" id="path3" /><path id="path4" style="color:{color};fill:#808080;stroke-linecap:round;-inkscape-stroke:none;stroke:none;stroke-opacity:1;fill-opacity:1" d="M 45.097656,5.0019531 A 5,5 0 0 0 43.177734,5.34375 5,5 0 0 0 40.419922,7.9960938 L 35.865234,18.40625 c 3.405007,0.669609 6.469474,2.331825 8.867188,4.679688 L 49.580078,12.003906 A 5,5 0 0 0 47.003906,5.4199219 5,5 0 0 0 45.097656,5.0019531 Z M 22.177734,49.691406 5.4199219,87.996094 a 5,5 0 0 0 2.5761719,6.583984 5,5 0 0 0 6.5839842,-2.576172 L 31.621094,53.052734 c -3.513941,-0.175553 -6.76611,-1.396873 -9.44336,-3.361328 z" /><path style="fill:#808080;fill-opacity:1;stroke:{color};stroke-width:0;stroke-linecap:round;stroke-opacity:1" id="path9" sodipodi:type="arc" sodipodi:cx="32.616085" sodipodi:cy="35.55938" sodipodi:rx="12.741771" sodipodi:ry="12.741771" sodipodi:start="0" sodipodi:end="6.26046" sodipodi:open="true" sodipodi:arc-type="arc" d="M 45.357856,35.55938 A 12.741771,12.741771 0 0 1 32.688475,48.300945 12.741771,12.741771 0 0 1 19.875137,35.704157 12.741771,12.741771 0 0 1 32.398925,22.81946 12.741771,12.741771 0 0 1 45.354566,35.269844" /></g><path style="fill:{color};fill-opacity:1;stroke:{color};stroke-width:0;stroke-linecap:round;stroke-opacity:1" id="path10" sodipodi:type="arc" sodipodi:cx="-106.61823" sodipodi:cy="26.136267" sodipodi:rx="12.741771" sodipodi:ry="12.741771" sodipodi:start="0" sodipodi:end="6.26046" sodipodi:open="true" sodipodi:arc-type="arc" d="m -93.876462,26.136267 a 12.741771,12.741771 0 0 1 -12.669378,12.741565 12.741771,12.741771 0 0 1 -12.81334,-12.596788 12.741771,12.741771 0 0 1 12.52379,-12.884697 12.741771,12.741771 0 0 1 12.955638,12.450384" transform="scale(-1,1)" /></g></svg>
|
Loading…
Reference in a new issue