forked from MapComplete/MapComplete
First draft of loading 'notes'
This commit is contained in:
parent
bafaba7011
commit
ebb510da04
20 changed files with 580 additions and 199 deletions
|
@ -93,7 +93,7 @@ export default class GeoJsonSource implements FeatureSourceForLayer, Tiled {
|
|||
for (const key in props) {
|
||||
if (typeof props[key] !== "string") {
|
||||
// Make sure all the values are string, it crashes stuff otherwise
|
||||
props[key] = "" + props[key]
|
||||
props[key] = JSON.stringify(props[key])
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -104,8 +104,8 @@ export default class MetaTagging {
|
|||
}
|
||||
return atLeastOneFeatureChanged
|
||||
}
|
||||
public static createFunctionsForFeature(layerId: string, calculatedTags: [string, string, boolean][]): ((feature: any) => boolean)[] {
|
||||
const functions: ((feature: any) => boolean)[] = [];
|
||||
public static createFunctionsForFeature(layerId: string, calculatedTags: [string, string, boolean][]): ((feature: any) => void)[] {
|
||||
const functions: ((feature: any) => any)[] = [];
|
||||
|
||||
for (const entry of calculatedTags) {
|
||||
const key = entry[0]
|
||||
|
@ -115,9 +115,8 @@ export default class MetaTagging {
|
|||
continue;
|
||||
}
|
||||
|
||||
const calculateAndAssign: ((feat: any) => boolean) = (feat) => {
|
||||
const calculateAndAssign: ((feat: any) => any) = (feat) => {
|
||||
try {
|
||||
let oldValue = isStrict ? feat.properties[key] : undefined
|
||||
let result = new Function("feat", "return " + code + ";")(feat);
|
||||
if (result === "") {
|
||||
result === undefined
|
||||
|
@ -128,7 +127,7 @@ export default class MetaTagging {
|
|||
}
|
||||
delete feat.properties[key]
|
||||
feat.properties[key] = result;
|
||||
return result === oldValue;
|
||||
return result
|
||||
}catch(e){
|
||||
if (MetaTagging.errorPrintCount < MetaTagging.stopErrorOutputAt) {
|
||||
console.warn("Could not calculate a " + (isStrict ? "strict " : "") + " calculated tag for key " + key + " defined by " + code + " (in layer" + layerId + ") due to \n" + e + "\n. Are you the theme creator? Doublecheck your code. Note that the metatags might not be stable on new features", e, e.stack)
|
||||
|
@ -137,7 +136,7 @@ export default class MetaTagging {
|
|||
console.error("Got ", MetaTagging.stopErrorOutputAt, " errors calculating this metatagging - stopping output now")
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -154,11 +153,10 @@ export default class MetaTagging {
|
|||
configurable: true,
|
||||
enumerable: false, // By setting this as not enumerable, the localTileSaver will _not_ calculate this
|
||||
get: function () {
|
||||
calculateAndAssign(feature)
|
||||
return feature.properties[key]
|
||||
return calculateAndAssign(feature)
|
||||
}
|
||||
})
|
||||
return true
|
||||
return undefined
|
||||
}
|
||||
|
||||
|
||||
|
@ -167,7 +165,7 @@ export default class MetaTagging {
|
|||
return functions;
|
||||
}
|
||||
|
||||
private static retaggingFuncCache = new Map<string, ((feature: any) => boolean)[]>()
|
||||
private static retaggingFuncCache = new Map<string, ((feature: any) => void)[]>()
|
||||
|
||||
/**
|
||||
* Creates the function which adds all the calculated tags to a feature. Called once per layer
|
||||
|
@ -183,7 +181,7 @@ export default class MetaTagging {
|
|||
return undefined;
|
||||
}
|
||||
|
||||
let functions :((feature: any) => boolean)[] = MetaTagging.retaggingFuncCache.get(layer.id);
|
||||
let functions :((feature: any) => void)[] = MetaTagging.retaggingFuncCache.get(layer.id);
|
||||
if (functions === undefined) {
|
||||
functions = MetaTagging.createFunctionsForFeature(layer.id, calculatedTags)
|
||||
MetaTagging.retaggingFuncCache.set(layer.id, functions)
|
||||
|
|
|
@ -218,6 +218,24 @@ export class OsmConnection {
|
|||
});
|
||||
}
|
||||
|
||||
public closeNote(id: number | string): Promise<any> {
|
||||
return new Promise((ok, error) => {
|
||||
this.auth.xhr({
|
||||
method: 'POST',
|
||||
path: `/api/0.6/notes/${id}/close`
|
||||
}, function (err, response) {
|
||||
console.log("Closing note gave:", err, response)
|
||||
if (err !== null) {
|
||||
error(err)
|
||||
} else {
|
||||
ok()
|
||||
}
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
private updateAuthObject() {
|
||||
let pwaStandAloneMode = false;
|
||||
try {
|
||||
|
@ -260,6 +278,4 @@ export class OsmConnection {
|
|||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -16,7 +16,7 @@ export class Tiles {
|
|||
const result: T[] = []
|
||||
const total = tileRange.total
|
||||
if (total > 100000) {
|
||||
throw "Tilerange too big"
|
||||
throw "Tilerange too big (z is "+tileRange.zoomlevel+")"
|
||||
}
|
||||
for (let x = tileRange.xstart; x <= tileRange.xend; x++) {
|
||||
for (let y = tileRange.ystart; y <= tileRange.yend; y++) {
|
||||
|
|
|
@ -43,7 +43,7 @@ export default class UserBadge extends Toggle {
|
|||
if (home === undefined) {
|
||||
return;
|
||||
}
|
||||
state.leafletMap.data.setView([home.lat, home.lon], 16);
|
||||
state.leafletMap.data?.setView([home.lat, home.lon], 16);
|
||||
});
|
||||
|
||||
const linkStyle = "flex items-baseline"
|
||||
|
|
|
@ -298,12 +298,16 @@ export class OH {
|
|||
}
|
||||
}
|
||||
|
||||
static Parse(rules: string) {
|
||||
public static simplify(str: string): string{
|
||||
return OH.ToString(OH.MergeTimes(OH.Parse(str)))
|
||||
}
|
||||
|
||||
public static Parse(rules: string) : OpeningHour[] {
|
||||
if (rules === undefined || rules === "") {
|
||||
return []
|
||||
}
|
||||
|
||||
const ohs = []
|
||||
const ohs : OpeningHour[] = []
|
||||
|
||||
const split = rules.split(";");
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ export default class FeatureInfoBox extends ScrollableFullScreen {
|
|||
.SetClass("break-words font-bold sm:p-0.5 md:p-1 sm:p-1.5 md:p-2");
|
||||
const titleIcons = new Combine(
|
||||
layerConfig.titleIcons.map(icon => new TagRenderingAnswer(tags, icon,
|
||||
"block w-8 h-8 align-baseline box-content sm:p-0.5", "width: 2rem;")
|
||||
"block w-8 h-8 max-h-8 align-baseline box-content sm:p-0.5", "width: 2rem;")
|
||||
))
|
||||
.SetClass("flex flex-row flex-wrap pt-0.5 sm:pt-1 items-center mr-2")
|
||||
|
||||
|
@ -193,9 +193,9 @@ export default class FeatureInfoBox extends ScrollableFullScreen {
|
|||
const config_download: TagRenderingConfig = new TagRenderingConfig({render: "{export_as_geojson()}"}, "");
|
||||
const config_id: TagRenderingConfig = new TagRenderingConfig({render: "{open_in_iD()}"}, "");
|
||||
|
||||
return new Combine([new TagRenderingAnswer(tags, config_all_tags, "all_tags"),
|
||||
new TagRenderingAnswer(tags, config_download, ""),
|
||||
new TagRenderingAnswer(tags, config_id, "")])
|
||||
return new Combine([new TagRenderingAnswer(tags, config_all_tags, State.state),
|
||||
new TagRenderingAnswer(tags, config_download, State.state),
|
||||
new TagRenderingAnswer(tags, config_id, State.state)])
|
||||
}
|
||||
})
|
||||
)
|
||||
|
|
|
@ -38,6 +38,7 @@ import TagApplyButton from "./Popup/TagApplyButton";
|
|||
import AutoApplyButton from "./Popup/AutoApplyButton";
|
||||
import * as left_right_style_json from "../assets/layers/left_right_style/left_right_style.json";
|
||||
import {OpenIdEditor} from "./BigComponents/CopyrightPanel";
|
||||
import Toggle from "./Input/Toggle";
|
||||
|
||||
export interface SpecialVisualization {
|
||||
funcName: string,
|
||||
|
@ -607,6 +608,39 @@ export default class SpecialVisualizations {
|
|||
Hash.hash.setData(undefined)
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
funcName: "close_note",
|
||||
docs: "Button to close a note",
|
||||
args:[
|
||||
{
|
||||
name:"text",
|
||||
doc: "Text to show on this button",
|
||||
},
|
||||
{
|
||||
name:"Id-key",
|
||||
doc: "The property name where the ID of the note to close can be found",
|
||||
defaultValue: "id"
|
||||
}
|
||||
],
|
||||
constr: (state, tags, args, guiState) => {
|
||||
const t = Translations.t.notes;
|
||||
const closeButton = new SubtleButton( Svg.checkmark_svg(), t.closeNote)
|
||||
const isClosed = new UIEventSource(false);
|
||||
closeButton.onClick(() => {
|
||||
const id = tags.data[args[1] ?? "id"]
|
||||
if(state.featureSwitchIsTesting.data){
|
||||
console.log("Not actually closing note...")
|
||||
return;
|
||||
}
|
||||
state.osmConnection.closeNote(id).then(_ => isClosed.setData(true))
|
||||
})
|
||||
return new Toggle(
|
||||
t.isClosed.SetClass("thanks"),
|
||||
closeButton,
|
||||
isClosed
|
||||
)
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
|
|
18
assets/themes/notes/license_info.json
Normal file
18
assets/themes/notes/license_info.json
Normal file
|
@ -0,0 +1,18 @@
|
|||
[
|
||||
{
|
||||
"path": "note.svg",
|
||||
"license": "CC0",
|
||||
"authors": [
|
||||
"Pieter Vander Vennet"
|
||||
],
|
||||
"sources": []
|
||||
},
|
||||
{
|
||||
"path": "resolved.svg",
|
||||
"license": "CC0",
|
||||
"authors": [
|
||||
"Pieter Vander Vennet"
|
||||
],
|
||||
"sources": []
|
||||
}
|
||||
]
|
45
assets/themes/notes/note.svg
Normal file
45
assets/themes/notes/note.svg
Normal file
|
@ -0,0 +1,45 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="275px"
|
||||
height="374px"
|
||||
viewBox="0 0 275 374"
|
||||
version="1.1"
|
||||
id="svg7"
|
||||
sodipodi:docname="teardrop.svg"
|
||||
inkscape:version="1.1.1 (1:1.1+202109281949+c3084ef5ed)"
|
||||
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="defs11" />
|
||||
<sodipodi:namedview
|
||||
id="namedview9"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#eeeeee"
|
||||
borderopacity="1"
|
||||
inkscape:pageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
showgrid="false"
|
||||
showguides="true"
|
||||
inkscape:guide-bbox="true"
|
||||
inkscape:snap-global="false"
|
||||
inkscape:zoom="1.8073494"
|
||||
inkscape:cx="80.227983"
|
||||
inkscape:cy="144.13372"
|
||||
inkscape:current-layer="svg7">
|
||||
<sodipodi:guide
|
||||
position="137.32093,125.42369"
|
||||
orientation="1,0"
|
||||
id="guide2177" />
|
||||
</sodipodi:namedview>
|
||||
<path
|
||||
id="path2"
|
||||
style="fill:#c60000;fill-opacity:1;fill-rule:nonzero;stroke:#950000;stroke-width:5;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 135.55664 1.9765625 C 74.593645 2.1922681 20.084472 43.910648 7.2773438 104.37109 C 4.936895 115.41704 4.1435422 126.42334 4.7265625 137.18555 C 2.5821353 205.89923 56.815085 269.30423 107.08203 335.96094 C 119.40234 351.22656 136.60547 368.02734 137.43359 370.03516 L 165.07812 337.42188 C 227.98632 255.40992 319.84389 135.74148 238.04492 49.080078 C 219.42005 26.853995 193.07863 10.556457 161.97656 4.4921875 C 153.10693 2.7626953 144.26564 1.9457474 135.55664 1.9765625 z " />
|
||||
<path
|
||||
id="path5936"
|
||||
style="color:#000000;fill:#fffffd;stroke:#000000;stroke-width:5;stroke-linecap:round;stroke-opacity:1;fill-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
d="m 202.54602,49.933374 a 19.846225,19.846225 0 0 0 -14.03347,5.812789 L 137.36365,106.89506 86.838795,56.370195 a 19.845967,19.845967 0 0 0 -14.033473,-5.812787 19.845967,19.845967 0 0 0 -14.033471,5.812787 19.845967,19.845967 0 0 0 0,28.066944 l 50.524859,50.524871 -50.328371,50.32837 a 19.846225,19.846225 0 0 0 0,28.06695 19.846225,19.846225 0 0 0 28.066944,0 l 50.328367,-50.32838 50.95423,50.95424 a 19.845967,19.845967 0 0 0 28.06694,0 19.845967,19.845967 0 0 0 0,-28.06695 L 165.4306,134.96201 216.57949,83.813106 a 19.846225,19.846225 0 0 0 0,-28.066943 19.846225,19.846225 0 0 0 -14.03347,-5.812789 z" />
|
||||
</svg>
|
After Width: | Height: | Size: 2.5 KiB |
93
assets/themes/notes/notes.json
Normal file
93
assets/themes/notes/notes.json
Normal file
|
@ -0,0 +1,93 @@
|
|||
{
|
||||
"id": "notes",
|
||||
"language": [
|
||||
"en"
|
||||
],
|
||||
"maintainer": "MapComplete",
|
||||
"startLat": 0,
|
||||
"startLon": 0,
|
||||
"startZoom": 0,
|
||||
"title": "Notes on OpenStreetMap",
|
||||
"version": "0.1",
|
||||
"description": "Notes from OpenStreetMap",
|
||||
"icon": "./assets/themes/notes/resolved.svg",
|
||||
"clustering": false,
|
||||
"layers": [
|
||||
{
|
||||
"id": "notes",
|
||||
"name": {
|
||||
"en": "OpenStreetMap notes"
|
||||
},
|
||||
"description": "Notes on OpenStreetMap.org",
|
||||
"source": {
|
||||
"osmTags": "id~*",
|
||||
"geoJson": "https://api.openstreetmap.org/api/0.6/notes.json?closed=7&bbox={x_min},{y_min},{x_max},{y_max}",
|
||||
"geoJsonZoomLevel": 12,
|
||||
"maxCacheAge": 0
|
||||
},
|
||||
"minzoom": 10,
|
||||
"title": {
|
||||
"render": {
|
||||
"en": "Note"
|
||||
},
|
||||
"mappings": [{
|
||||
"if": "closed_at~*",
|
||||
"then": {
|
||||
"en": "Closed note"
|
||||
}
|
||||
}]
|
||||
},
|
||||
"calculatedTags": [
|
||||
"_first_comment:=feat.get('comments')[0].text",
|
||||
"_conversation=feat.get('comments').map(c => {if(c.user_url == undefined) {return 'anonymous user, '+c.date;} return c.html+'<div class=\"subtle flex justify-end border-b border-gray-500\"><a href=\"'+c.user_url+'\" target=\"_blank\">'+c.user+'</a> '+c.date+'</div>'}).join('')"
|
||||
],
|
||||
"titleIcons": [{
|
||||
"render": "<a href='https://openstreetmap.org/note/{id}' target='_blank'><img src='./assets/svg/osm-logo-us.svg'></a>"
|
||||
}],
|
||||
"tagRenderings": [
|
||||
{
|
||||
"id": "conversation",
|
||||
"render": "{_conversation}"
|
||||
},
|
||||
{
|
||||
"id": "date_created",
|
||||
"render": {
|
||||
"en": "Opened on {date_created}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "close",
|
||||
"render": "{close_note()}",
|
||||
"condition": "closed_at="
|
||||
}
|
||||
],
|
||||
"mapRendering": [
|
||||
{
|
||||
"location": [
|
||||
"point",
|
||||
"centroid"
|
||||
],
|
||||
"icon": {
|
||||
"render": "./assets/themes/notes/note.svg",
|
||||
"mappings": [
|
||||
{
|
||||
"if": "closed_at~*",
|
||||
"then": "./assets/themes/notes/resolved.svg"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
"iconSize": "40,40,bottom"
|
||||
}
|
||||
],
|
||||
"filter": [{
|
||||
"id": "bookcases",
|
||||
"options": [
|
||||
{
|
||||
"osmTags": "_first_comment~.*bookcase.*",
|
||||
"question": "Should mention 'bookcase' in the first comment"
|
||||
}]
|
||||
}]
|
||||
}
|
||||
]
|
||||
}
|
46
assets/themes/notes/resolved.svg
Normal file
46
assets/themes/notes/resolved.svg
Normal file
|
@ -0,0 +1,46 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="275px"
|
||||
height="374px"
|
||||
viewBox="0 0 275 374"
|
||||
version="1.1"
|
||||
id="svg7"
|
||||
sodipodi:docname="resolved.svg"
|
||||
inkscape:version="1.1.1 (1:1.1+202109281949+c3084ef5ed)"
|
||||
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="defs11" />
|
||||
<sodipodi:namedview
|
||||
id="namedview9"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#eeeeee"
|
||||
borderopacity="1"
|
||||
inkscape:pageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
showgrid="false"
|
||||
showguides="true"
|
||||
inkscape:guide-bbox="true"
|
||||
inkscape:snap-global="false"
|
||||
inkscape:zoom="2.2793262"
|
||||
inkscape:cx="68.44128"
|
||||
inkscape:cy="162.54804"
|
||||
inkscape:current-layer="svg7">
|
||||
<sodipodi:guide
|
||||
position="137.32093,125.42369"
|
||||
orientation="1,0"
|
||||
id="guide2177" />
|
||||
</sodipodi:namedview>
|
||||
<path
|
||||
id="path2"
|
||||
style="fill:#00c82e;fill-opacity:1;fill-rule:nonzero;stroke:#40ff00;stroke-width:5;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 135.55664 1.9765625 C 74.593645 2.1922681 20.084472 43.910648 7.2773438 104.37109 C 4.936895 115.41704 4.1435422 126.42334 4.7265625 137.18555 C 2.5821353 205.89923 56.815085 269.30423 107.08203 335.96094 C 119.40234 351.22656 136.60547 368.02734 137.43359 370.03516 L 165.07812 337.42188 C 227.98632 255.40992 319.84389 135.74148 238.04492 49.080078 C 219.42005 26.853995 193.07863 10.556457 161.97656 4.4921875 C 153.10693 2.7626953 144.26564 1.9457474 135.55664 1.9765625 z " />
|
||||
<path
|
||||
style="color:#000000;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:3.52437;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 216.35763,81.457377 c -6.31364,-1.014444 -12.65279,1.657101 -16.62944,7.00824 L 117.3564,199.32492 69.393978,152.33285 c -7.532586,-9.1813 -20.4662,-9.07329 -27.311559,-1.91781 -7.087039,7.56459 -8.507731,17.24412 -0.648456,25.76692 l 61.942427,65.80658 c 7.15464,9.62634 20.80539,9.62634 27.96003,0 l 96.3518,-129.67383 c 6.14683,-8.27231 4.87072,-20.317183 -2.85026,-26.902908 -2.45389,-2.092661 -5.37864,-3.456485 -8.48033,-3.954425 z"
|
||||
id="path3639"
|
||||
sodipodi:nodetypes="ccccccccccc" />
|
||||
</svg>
|
After Width: | Height: | Size: 2.4 KiB |
|
@ -716,18 +716,6 @@ video {
|
|||
left: 0px;
|
||||
}
|
||||
|
||||
.bottom-3 {
|
||||
bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.left-3 {
|
||||
left: 0.75rem;
|
||||
}
|
||||
|
||||
.right-2 {
|
||||
right: 0.5rem;
|
||||
}
|
||||
|
||||
.left-24 {
|
||||
left: 6rem;
|
||||
}
|
||||
|
@ -740,6 +728,18 @@ video {
|
|||
top: 14rem;
|
||||
}
|
||||
|
||||
.bottom-3 {
|
||||
bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.left-3 {
|
||||
left: 0.75rem;
|
||||
}
|
||||
|
||||
.right-2 {
|
||||
right: 0.5rem;
|
||||
}
|
||||
|
||||
.top-2 {
|
||||
top: 0.5rem;
|
||||
}
|
||||
|
@ -800,10 +800,6 @@ video {
|
|||
margin: 2rem;
|
||||
}
|
||||
|
||||
.m-11 {
|
||||
margin: 2.75rem;
|
||||
}
|
||||
|
||||
.m-1 {
|
||||
margin: 0.25rem;
|
||||
}
|
||||
|
@ -812,6 +808,10 @@ video {
|
|||
margin: 1.25rem;
|
||||
}
|
||||
|
||||
.m-4 {
|
||||
margin: 1rem;
|
||||
}
|
||||
|
||||
.m-0\.5 {
|
||||
margin: 0.125rem;
|
||||
}
|
||||
|
@ -824,10 +824,6 @@ video {
|
|||
margin: 0.75rem;
|
||||
}
|
||||
|
||||
.m-4 {
|
||||
margin: 1rem;
|
||||
}
|
||||
|
||||
.m-2 {
|
||||
margin: 0.5rem;
|
||||
}
|
||||
|
@ -1080,6 +1076,10 @@ video {
|
|||
width: 2.75rem;
|
||||
}
|
||||
|
||||
.w-6 {
|
||||
width: 1.5rem;
|
||||
}
|
||||
|
||||
.w-16 {
|
||||
width: 4rem;
|
||||
}
|
||||
|
@ -1090,10 +1090,6 @@ video {
|
|||
width: min-content;
|
||||
}
|
||||
|
||||
.w-6 {
|
||||
width: 1.5rem;
|
||||
}
|
||||
|
||||
.w-max {
|
||||
width: -webkit-max-content;
|
||||
width: -moz-max-content;
|
||||
|
@ -1565,6 +1561,10 @@ video {
|
|||
text-transform: lowercase;
|
||||
}
|
||||
|
||||
.capitalize {
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
.italic {
|
||||
font-style: italic;
|
||||
}
|
||||
|
|
|
@ -62,5 +62,9 @@
|
|||
"sizes": "513x513",
|
||||
"type": "image/svg"
|
||||
}
|
||||
],
|
||||
"categories": [
|
||||
"map",
|
||||
"navigation"
|
||||
]
|
||||
}
|
|
@ -422,5 +422,10 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"notes": {
|
||||
"isClosed": "This note is resolved",
|
||||
"closeNote":
|
||||
"Close this note"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -944,6 +944,18 @@
|
|||
"shortDescription": "This map shows the nature reserves of Natuurpunt",
|
||||
"title": "The map of Natuurpunt"
|
||||
},
|
||||
"notes": {
|
||||
"layers": {
|
||||
"0": {
|
||||
"name": "OpenStreetMap notes",
|
||||
"tagRenderings": {
|
||||
"date_created": {
|
||||
"render": "Opened on {date_created}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"observation_towers": {
|
||||
"description": "Publicly accessible towers to enjoy the view",
|
||||
"shortDescription": "Publicly accessible towers to enjoy the view",
|
||||
|
|
|
@ -42,7 +42,8 @@
|
|||
"gittag": "ts-node scripts/printVersion.ts | bash",
|
||||
"lint": "tslint --project . -c tslint.json '**.ts' ",
|
||||
"clean": "rm -rf .cache/ && (find *.html | grep -v \"\\(404\\|index\\|land\\|test\\|preferences\\|customGenerator\\|professional\\|automaton\\|theme\\).html\" | xargs rm) && (ls | grep \"^index_[a-zA-Z_]\\+\\.ts$\" | xargs rm) && (ls | grep \".*.webmanifest$\" | xargs rm)",
|
||||
"generate:dependency-graph": "node_modules/.bin/depcruise --exclude \"^node_modules\" --output-type dot Logic/State/MapState.ts > dependencies.dot && dot dependencies.dot -T svg -o dependencies.svg && rm dependencies.dot"
|
||||
"generate:dependency-graph": "node_modules/.bin/depcruise --exclude \"^node_modules\" --output-type dot Logic/State/MapState.ts > dependencies.dot && dot dependencies.dot -T svg -o dependencies.svg && rm dependencies.dot",
|
||||
"bicycle_rental": "ts-node ./scripts/extractBikeRental.ts"
|
||||
},
|
||||
"keywords": [
|
||||
"OpenStreetMap",
|
||||
|
|
211
scripts/extractBikeRental.ts
Normal file
211
scripts/extractBikeRental.ts
Normal file
|
@ -0,0 +1,211 @@
|
|||
import * as fs from "fs";
|
||||
import {OH} from "../UI/OpeningHours/OpeningHours";
|
||||
|
||||
|
||||
function extractValue(vs: { __value }[]) {
|
||||
if(vs === undefined){
|
||||
return undefined
|
||||
}
|
||||
for (const v of vs) {
|
||||
if ((v.__value ?? "") === "") {
|
||||
continue
|
||||
}
|
||||
return v.__value;
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
|
||||
function extract_oh_block (days) : string{
|
||||
const oh = []
|
||||
for (const day of days.day) {
|
||||
const abbr = day.name.substr(0,2)
|
||||
const block = day.time_block[0]
|
||||
const from = block.time_from.substr(0,5)
|
||||
const to = block.time_until.substr(0,5)
|
||||
const by_appointment = block.by_appointment ? " \"by appointment\"" : ""
|
||||
oh.push(`${abbr} ${from}-${to}${by_appointment}`)
|
||||
}
|
||||
return oh.join("; ")
|
||||
}
|
||||
function extract_oh(opening_periods){
|
||||
const rules = []
|
||||
if(opening_periods === undefined){
|
||||
return undefined;
|
||||
}
|
||||
for (const openingPeriod of opening_periods.opening_period ?? []) {
|
||||
let rule = extract_oh_block(openingPeriod.days)
|
||||
if(openingPeriod.name.toLowerCase().indexOf("schoolvakantie") >= 0){
|
||||
rule = "SH "+rule
|
||||
}
|
||||
rules.push(rule)
|
||||
}
|
||||
return OH.simplify( rules.join(";"))
|
||||
}
|
||||
|
||||
function rewrite(obj, key) {
|
||||
if (obj[key] === undefined) {
|
||||
return
|
||||
}
|
||||
obj[key] = extractValue(obj[key]["value"])
|
||||
}
|
||||
|
||||
const stuff = fs.readFileSync("/home/pietervdvn/Documents/Freelance/ToerismeVlaanderen 2021-09/TeImporteren/allchannels-bike_rental.json", "UTF8")
|
||||
const data: any[] = JSON.parse(stuff)
|
||||
|
||||
const results: {
|
||||
geometry: {
|
||||
type: "Point",
|
||||
coordinates: [number, number]
|
||||
},
|
||||
type: "Feature",
|
||||
properties: any
|
||||
|
||||
}[] = []
|
||||
const skipped = []
|
||||
console.log("[")
|
||||
for (const item of data) {
|
||||
const metadata = item["metadata"]
|
||||
|
||||
if (metadata.name === "Jommekeroute") {
|
||||
continue
|
||||
}
|
||||
|
||||
const addr = item.location_info?.address
|
||||
if (addr === undefined) {
|
||||
skipped.push(item)
|
||||
continue
|
||||
}
|
||||
const toDelete = ["id", "uuid", "update_date", "creation_date",
|
||||
"deleted",
|
||||
"aborted",
|
||||
"partner_id",
|
||||
"business_product_id",
|
||||
"winref",
|
||||
"winref_uuid",
|
||||
"root_product_type",
|
||||
"parent"
|
||||
]
|
||||
for (const key of toDelete) {
|
||||
delete metadata[key]
|
||||
}
|
||||
|
||||
delete item["meeting_rooms_count"]
|
||||
delete item["facilities"]
|
||||
|
||||
item.properties = metadata
|
||||
delete item["metadata"]
|
||||
|
||||
const metadata_values = ["touristic_product_type", "root_product_type"]
|
||||
for (const key of metadata_values) {
|
||||
rewrite(metadata, key)
|
||||
}
|
||||
|
||||
rewrite(item.contact_info, "commercial_name")
|
||||
|
||||
const gl = addr.geolocation
|
||||
item.coordinates = [gl.lon, gl.lat]
|
||||
metadata["addr:street"] = addr.street
|
||||
metadata["addr:housenumber"] = addr.number
|
||||
metadata["phone"] = item.contact_info["telephone"] ?? item.contact_info["mobile"]
|
||||
metadata["email"] = item.contact_info["email_address"]
|
||||
|
||||
const links = item.links?.link?.map(l => l.url) ?? []
|
||||
metadata["website"] = item.contact_info["website"] ?? links[0]
|
||||
|
||||
delete item["links"]
|
||||
|
||||
delete item.location_info
|
||||
delete item.contact_info
|
||||
delete item.promotional_info
|
||||
|
||||
if (metadata["touristic_product_type"] === "Fietsverhuur") {
|
||||
metadata["amenity"] = "bicycle_rental"
|
||||
delete metadata["touristic_product_type"]
|
||||
} else {
|
||||
console.error("Unkown product type: ", metadata["touristic_product_type"])
|
||||
}
|
||||
|
||||
const descriptions = item.descriptions?.description?.map(d => extractValue(d?.text?.value)) ?? []
|
||||
delete item.descriptions
|
||||
metadata["description"] = metadata["description"] ?? descriptions[0]
|
||||
if (item.price_info?.prices?.free == true) {
|
||||
metadata.fee = "no"
|
||||
delete item.price_info
|
||||
} else if (item.price_info?.prices?.free == false) {
|
||||
metadata.fee = "yes"
|
||||
metadata.charge = extractValue(item.price_info?.extra_information?.value)
|
||||
const methods = item.price_info?.payment_methods?.payment_method
|
||||
if(methods !== undefined){
|
||||
methods.map(v => extractValue(v.value)).forEach(method => {
|
||||
metadata["payment:" + method.toLowerCase()] = "yes"
|
||||
})
|
||||
}
|
||||
delete item.price_info
|
||||
}else if(item.price_info?.prices?.length === 0){
|
||||
delete item.price_info
|
||||
}
|
||||
|
||||
|
||||
try{
|
||||
|
||||
if(item.labels_info?.labels_own?.label[0]?.code === "Billenkar"){
|
||||
metadata.rental = "quadricycle"
|
||||
delete item.labels_info
|
||||
}
|
||||
}catch(e){
|
||||
|
||||
}
|
||||
delete item["publishing_channels"]
|
||||
|
||||
|
||||
try {
|
||||
metadata["image"] = item.media.file[0].url[0]
|
||||
} catch (e) {
|
||||
// No image!
|
||||
}
|
||||
delete item.media
|
||||
|
||||
|
||||
|
||||
const time_info = item.time_info?.time_info_regular
|
||||
if(time_info?.permantly_open === true){
|
||||
metadata.opening_hours = "24/7"
|
||||
}else{
|
||||
metadata.opening_hours = extract_oh(time_info?.opening_periods)
|
||||
}
|
||||
delete item.time_info
|
||||
|
||||
|
||||
|
||||
const properties = {}
|
||||
for (const key in metadata) {
|
||||
const v = metadata[key]
|
||||
if(v === null || v === undefined || v === ""){
|
||||
delete metadata[key]
|
||||
continue
|
||||
}
|
||||
properties[key] = v
|
||||
}
|
||||
results.push({
|
||||
geometry: {
|
||||
type: "Point",
|
||||
coordinates: item.coordinates
|
||||
},
|
||||
type: "Feature",
|
||||
properties
|
||||
})
|
||||
|
||||
delete item.coordinates
|
||||
delete item.properties
|
||||
console.log(JSON.stringify(item, null, " ") + ",")
|
||||
|
||||
}
|
||||
console.log("]")
|
||||
fs.writeFileSync("west-vlaanderen.geojson", JSON.stringify(
|
||||
{
|
||||
type: "FeatureCollection",
|
||||
features: results
|
||||
}
|
||||
, null, " "
|
||||
))
|
45
scripts/perProperty.ts
Normal file
45
scripts/perProperty.ts
Normal file
|
@ -0,0 +1,45 @@
|
|||
import * as fs from "fs";
|
||||
|
||||
function main(args){
|
||||
if(args.length < 2){
|
||||
console.log("Given a single geojson file, generates the partitions for every found property")
|
||||
console.log("USAGE: perProperty `file.geojson` `property-key`")
|
||||
return
|
||||
}
|
||||
const path = args[0]
|
||||
const key = args[1]
|
||||
|
||||
const data= JSON.parse(fs.readFileSync(path, "UTF8"))
|
||||
const perProperty = new Map<string, any[]>()
|
||||
|
||||
console.log("Partitioning",data.features.length, "features")
|
||||
for (const feature of data.features) {
|
||||
const v = feature.properties[key]
|
||||
if(!perProperty.has(v)){
|
||||
console.log("Found a new category:", v)
|
||||
perProperty.set(v, [])
|
||||
}
|
||||
perProperty.get(v).push(feature)
|
||||
}
|
||||
|
||||
const stripped = path.substr(0, path.length - ".geojson".length)
|
||||
perProperty.forEach((features, v) => {
|
||||
|
||||
fs.writeFileSync(stripped+"."+v.replace(/[^a-zA-Z0-9_]/g, "_")+".geojson",
|
||||
JSON.stringify({
|
||||
type:"FeatureCollection",
|
||||
features
|
||||
}))
|
||||
})
|
||||
|
||||
|
||||
}
|
||||
|
||||
let args = [...process.argv]
|
||||
args.splice(0, 2)
|
||||
try {
|
||||
main(args)
|
||||
} catch (e) {
|
||||
console.error("Error building cache:", e)
|
||||
}
|
||||
console.log("All done!")
|
153
test.ts
153
test.ts
|
@ -1,152 +1 @@
|
|||
import State from "./State";
|
||||
import AllKnownLayers from "./Customizations/AllKnownLayers";
|
||||
import AvailableBaseLayersImplementation from "./Logic/Actors/AvailableBaseLayersImplementation";
|
||||
import AvailableBaseLayers from "./Logic/Actors/AvailableBaseLayers";
|
||||
import MinimapImplementation from "./UI/Base/MinimapImplementation";
|
||||
import {Utils} from "./Utils";
|
||||
import * as grb from "./assets/themes/grb_import/grb.json";
|
||||
import ReplaceGeometryAction from "./Logic/Osm/Actions/ReplaceGeometryAction";
|
||||
import Minimap from "./UI/Base/Minimap";
|
||||
import ShowDataLayer from "./UI/ShowDataLayer/ShowDataLayer";
|
||||
import {AllKnownLayouts} from "./Customizations/AllKnownLayouts";
|
||||
import {BBox} from "./Logic/BBox";
|
||||
|
||||
AvailableBaseLayers.implement(new AvailableBaseLayersImplementation())
|
||||
MinimapImplementation.initialize()
|
||||
|
||||
async function test() {
|
||||
|
||||
const wayId = "way/323230330";
|
||||
const targetFeature = {
|
||||
"type": "Feature",
|
||||
"properties": {},
|
||||
"geometry": {
|
||||
"type": "Polygon",
|
||||
"coordinates": [
|
||||
[
|
||||
[
|
||||
4.483118100000016,
|
||||
51.028366499999706
|
||||
],
|
||||
[
|
||||
4.483135099999986,
|
||||
51.028325800000005
|
||||
],
|
||||
[
|
||||
4.483137700000021,
|
||||
51.02831960000019
|
||||
],
|
||||
[
|
||||
4.4831429000000025,
|
||||
51.0283205
|
||||
],
|
||||
[
|
||||
4.483262199999987,
|
||||
51.02834059999982
|
||||
],
|
||||
[
|
||||
4.483276700000019,
|
||||
51.028299999999746
|
||||
],
|
||||
[
|
||||
4.483342100000037,
|
||||
51.02830730000009
|
||||
],
|
||||
[
|
||||
4.483340700000012,
|
||||
51.028331299999934
|
||||
],
|
||||
[
|
||||
4.483346499999953,
|
||||
51.02833189999984
|
||||
],
|
||||
[
|
||||
4.483290600000001,
|
||||
51.028500699999846
|
||||
],
|
||||
[
|
||||
4.4833335999999635,
|
||||
51.02851150000015
|
||||
],
|
||||
[
|
||||
4.4833433000000475,
|
||||
51.028513999999944
|
||||
],
|
||||
[
|
||||
4.483312899999958,
|
||||
51.02857759999998
|
||||
],
|
||||
[
|
||||
4.483141100000033,
|
||||
51.02851780000015
|
||||
],
|
||||
[
|
||||
4.483193100000022,
|
||||
51.028409999999894
|
||||
],
|
||||
[
|
||||
4.483206100000019,
|
||||
51.02838310000014
|
||||
],
|
||||
[
|
||||
4.483118100000016,
|
||||
51.028366499999706
|
||||
]
|
||||
]
|
||||
]
|
||||
},
|
||||
"id": "https://betadata.grbosm.site/grb?bbox=498980.9206456306,6626173.107985358,499133.7947022009,6626325.98204193/30",
|
||||
"bbox": {
|
||||
"maxLat": 51.02857759999998,
|
||||
"maxLon": 4.483346499999953,
|
||||
"minLat": 51.028299999999746,
|
||||
"minLon": 4.483118100000016
|
||||
},
|
||||
"_lon": 4.483232299999985,
|
||||
"_lat": 51.02843879999986
|
||||
}
|
||||
|
||||
|
||||
const layout = AllKnownLayouts.allKnownLayouts.get("grb")
|
||||
const state = new State(layout)
|
||||
State.state = state;
|
||||
const bbox = new BBox(
|
||||
[[
|
||||
4.482952281832695,
|
||||
51.02828527958197
|
||||
],
|
||||
[
|
||||
4.483400881290436,
|
||||
51.028578384406984
|
||||
]
|
||||
|
||||
])
|
||||
const url = `https://www.openstreetmap.org/api/0.6/map.json?bbox=${bbox.minLon},${bbox.minLat},${bbox.maxLon},${bbox.maxLat}`
|
||||
const data = await Utils.downloadJson(url)
|
||||
|
||||
state.featurePipeline.fullNodeDatabase.handleOsmJson(data, 0)
|
||||
|
||||
|
||||
const action = new ReplaceGeometryAction(state, targetFeature, wayId, {
|
||||
theme: "test"
|
||||
}
|
||||
)
|
||||
|
||||
console.log(">>>>> ", action.GetClosestIds())
|
||||
|
||||
const map = Minimap.createMiniMap({
|
||||
attribution: false,
|
||||
})
|
||||
const preview = await action.getPreview()
|
||||
new ShowDataLayer({
|
||||
layerToShow: AllKnownLayers.sharedLayers.get("conflation"),
|
||||
features: preview,
|
||||
leafletMap: map.leafletMap,
|
||||
zoomToFeatures: true
|
||||
})
|
||||
map
|
||||
.SetStyle("height: 75vh;")
|
||||
.AttachTo("maindiv")
|
||||
}
|
||||
|
||||
test()
|
||||
console.log("Hello world")
|
Loading…
Reference in a new issue