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…
	
	Add table
		Add a link
		
	
		Reference in a new issue