forked from MapComplete/MapComplete
		
	Add export_as_geojson, open_in_id and open_in_josm special renderings
This commit is contained in:
		
							parent
							
								
									b0f0a57bc7
								
							
						
					
					
						commit
						7f829a3578
					
				
					 8 changed files with 144 additions and 60 deletions
				
			
		|  | @ -19,9 +19,82 @@ import Loc from "../../Models/Loc"; | |||
| import Toggle from "../Input/Toggle"; | ||||
| import {OsmConnection} from "../../Logic/Osm/OsmConnection"; | ||||
| import Constants from "../../Models/Constants"; | ||||
| import PrivacyPolicy from "./PrivacyPolicy"; | ||||
| import ContributorCount from "../../Logic/ContributorCount"; | ||||
| 
 | ||||
| export class OpenIdEditor extends VariableUiElement { | ||||
|     constructor(state : {locationControl: UIEventSource<Loc>}, iconStyle? : string, objectId?: string) { | ||||
|         const t = Translations.t.general.attribution | ||||
|         super(state.locationControl.map(location => { | ||||
|             let elementSelect = ""; | ||||
|             if(objectId !== undefined){ | ||||
|                const parts = objectId.split("/") | ||||
|                 const tp = parts[0] | ||||
|                 if(parts.length === 2 && !isNaN(Number(parts[1]))  && (tp === "node" || tp === "way" || tp === "relation")){ | ||||
|                     elementSelect = "&"+ tp+"="+parts[1] | ||||
|                 } | ||||
|             } | ||||
|             const idLink = `https://www.openstreetmap.org/edit?editor=id${elementSelect}#map=${location?.zoom ?? 0}/${location?.lat ?? 0}/${location?.lon ?? 0}` | ||||
|             return new SubtleButton(Svg.pencil_ui().SetStyle(iconStyle), t.editId, {url: idLink, newTab: true}) | ||||
|         })); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| export class OpenMapillary extends VariableUiElement { | ||||
|     constructor(state : {locationControl: UIEventSource<Loc>}, iconStyle? : string) { | ||||
|         const t = Translations.t.general.attribution | ||||
|        super( state.locationControl.map(location => { | ||||
|             const mapillaryLink = `https://www.mapillary.com/app/?focus=map&lat=${location?.lat ?? 0}&lng=${location?.lon ?? 0}&z=${Math.max((location?.zoom ?? 2) - 1, 1)}` | ||||
|             return new SubtleButton(Svg.mapillary_black_ui().SetStyle(iconStyle), t.openMapillary, { | ||||
|                 url: mapillaryLink, | ||||
|                 newTab: true | ||||
|             }) | ||||
|         })) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export class OpenJosm extends Combine { | ||||
| 
 | ||||
|     constructor(state : {osmConnection: OsmConnection, currentBounds: UIEventSource<BBox>,}, iconStyle? : string) { | ||||
|    const t = Translations.t.general.attribution | ||||
|       | ||||
|         const josmState = new UIEventSource<string>(undefined) | ||||
|         // Reset after 15s
 | ||||
|         josmState.stabilized(15000).addCallbackD(_ => josmState.setData(undefined)) | ||||
|          | ||||
|         const stateIndication = new VariableUiElement(josmState.map(state => { | ||||
|             if (state === undefined) { | ||||
|                 return undefined | ||||
|             } | ||||
|             state = state.toUpperCase() | ||||
|             if (state === "OK") { | ||||
|                 return t.josmOpened.SetClass("thanks") | ||||
|             } | ||||
|             return t.josmNotOpened.SetClass("alert") | ||||
|         })); | ||||
|          | ||||
|         const toggle =    new Toggle( | ||||
|                 new SubtleButton(Svg.josm_logo_ui().SetStyle(iconStyle), t.editJosm).onClick(() => { | ||||
|                     const bounds: any = state.currentBounds.data; | ||||
|                     if (bounds === undefined) { | ||||
|                         return undefined | ||||
|                     } | ||||
|                     const top = bounds.getNorth(); | ||||
|                     const bottom = bounds.getSouth(); | ||||
|                     const right = bounds.getEast(); | ||||
|                     const left = bounds.getWest(); | ||||
|                     const josmLink = `http://127.0.0.1:8111/load_and_zoom?left=${left}&right=${right}&top=${top}&bottom=${bottom}` | ||||
|                     Utils.download(josmLink).then(answer => josmState.setData(answer.replace(/\n/g, '').trim())).catch(_ => josmState.setData("ERROR")) | ||||
|                 }), undefined, state.osmConnection.userDetails.map(ud => ud.loggedIn && ud.csCount >= Constants.userJourney.historyLinkVisible)) | ||||
| 
 | ||||
|         super([stateIndication, toggle]); | ||||
|          | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /** | ||||
|  * The attribution panel shown on mobile | ||||
|  */ | ||||
|  | @ -39,10 +112,7 @@ export default class CopyrightPanel extends Combine { | |||
| 
 | ||||
|         const t = Translations.t.general.attribution | ||||
|         const layoutToUse = state.layoutToUse | ||||
|         const josmState = new UIEventSource<string>(undefined) | ||||
|         // Reset after 15s
 | ||||
|         josmState.stabilized(15000).addCallbackD(_ => josmState.setData(undefined)) | ||||
|         const iconStyle = "height: 1.5rem; width: auto" | ||||
|           const iconStyle = "height: 1.5rem; width: auto" | ||||
|         const actionButtons = [ | ||||
|             new SubtleButton(Svg.liberapay_ui().SetStyle(iconStyle), t.donate, { | ||||
|                 url: "https://liberapay.com/pietervdvn/", | ||||
|  | @ -56,42 +126,9 @@ export default class CopyrightPanel extends Combine { | |||
|                 url: Utils.OsmChaLinkFor(31, state.layoutToUse.id), | ||||
|                 newTab: true | ||||
|             }), | ||||
|             new VariableUiElement(state.locationControl.map(location => { | ||||
|                 const idLink = `https://www.openstreetmap.org/edit?editor=id#map=${location?.zoom ?? 0}/${location?.lat ?? 0}/${location?.lon ?? 0}` | ||||
|                 return new SubtleButton(Svg.pencil_ui().SetStyle(iconStyle), t.editId, {url: idLink, newTab: true}) | ||||
|             })), | ||||
| 
 | ||||
|             new VariableUiElement(state.locationControl.map(location => { | ||||
|                 const mapillaryLink = `https://www.mapillary.com/app/?focus=map&lat=${location?.lat ?? 0}&lng=${location?.lon ?? 0}&z=${Math.max((location?.zoom ?? 2) - 1, 1)}` | ||||
|                 return new SubtleButton(Svg.mapillary_black_ui().SetStyle(iconStyle), t.openMapillary, { | ||||
|                     url: mapillaryLink, | ||||
|                     newTab: true | ||||
|                 }) | ||||
|             })), | ||||
|             new VariableUiElement(josmState.map(state => { | ||||
|                 if (state === undefined) { | ||||
|                     return undefined | ||||
|                 } | ||||
|                 state = state.toUpperCase() | ||||
|                 if (state === "OK") { | ||||
|                     return t.josmOpened.SetClass("thanks") | ||||
|                 } | ||||
|                 return t.josmNotOpened.SetClass("alert") | ||||
|             })), | ||||
|             new Toggle( | ||||
|                 new SubtleButton(Svg.josm_logo_ui().SetStyle(iconStyle), t.editJosm).onClick(() => { | ||||
|                     const bounds: any = state.currentBounds.data; | ||||
|                     if (bounds === undefined) { | ||||
|                         return undefined | ||||
|                     } | ||||
|                     const top = bounds.getNorth(); | ||||
|                     const bottom = bounds.getSouth(); | ||||
|                     const right = bounds.getEast(); | ||||
|                     const left = bounds.getWest(); | ||||
|                     const josmLink = `http://127.0.0.1:8111/load_and_zoom?left=${left}&right=${right}&top=${top}&bottom=${bottom}` | ||||
|                     Utils.download(josmLink).then(answer => josmState.setData(answer.replace(/\n/g, '').trim())).catch(_ => josmState.setData("ERROR")) | ||||
|                 }), undefined, state.osmConnection.userDetails.map(ud => ud.loggedIn && ud.csCount >= Constants.userJourney.historyLinkVisible)), | ||||
| 
 | ||||
|             new OpenIdEditor(state, iconStyle), | ||||
|             new OpenMapillary(state, iconStyle), | ||||
|             new OpenJosm(state, iconStyle) | ||||
|         ] | ||||
| 
 | ||||
|         const iconAttributions = Utils.NoNull(Array.from(layoutToUse.ExtractImages())) | ||||
|  |  | |||
|  | @ -18,7 +18,6 @@ import {Utils} from "../../Utils"; | |||
| import {SubstitutedTranslation} from "../SubstitutedTranslation"; | ||||
| import MoveWizard from "./MoveWizard"; | ||||
| import Toggle from "../Input/Toggle"; | ||||
| import {FixedUiElement} from "../Base/FixedUiElement"; | ||||
| 
 | ||||
| export default class FeatureInfoBox extends ScrollableFullScreen { | ||||
| 
 | ||||
|  | @ -189,8 +188,13 @@ export default class FeatureInfoBox extends ScrollableFullScreen { | |||
|             new VariableUiElement( | ||||
|                 State.state.featureSwitchIsDebugging.map(isDebugging => { | ||||
|                     if (isDebugging) { | ||||
|                         const config: TagRenderingConfig = new TagRenderingConfig({render: "{all_tags()}"}, ""); | ||||
|                         return new TagRenderingAnswer(tags, config, "all_tags") | ||||
|                         const config_all_tags: TagRenderingConfig = new TagRenderingConfig({render: "{all_tags()}"}, ""); | ||||
|                         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, "")]) | ||||
|                     } | ||||
|                 }) | ||||
|             ) | ||||
|  |  | |||
|  | @ -37,6 +37,7 @@ import FeaturePipelineState from "../Logic/State/FeaturePipelineState"; | |||
| import {ConflateButton, ImportPointButton, ImportWayButton} from "./Popup/ImportButton"; | ||||
| import TagApplyButton from "./Popup/TagApplyButton"; | ||||
| import AutoApplyButton from "./Popup/AutoApplyButton"; | ||||
| import {OpenIdEditor} from "./BigComponents/CopyrightPanel"; | ||||
| 
 | ||||
| export interface SpecialVisualization { | ||||
|     funcName: string, | ||||
|  | @ -543,7 +544,7 @@ export default class SpecialVisualizations { | |||
|                         const t = Translations.t.general.download; | ||||
| 
 | ||||
|                         return new SubtleButton(Svg.download_ui(), | ||||
|                             new Combine([t.downloadGpx.SetClass("font-bold text-lg"), | ||||
|                             new Combine([t.downloadFeatureAsGpx.SetClass("font-bold text-lg"), | ||||
|                                 t.downloadGpxHelper.SetClass("subtle")]).SetClass("flex flex-col") | ||||
|                         ).onClick(() => { | ||||
|                             console.log("Exporting as GPX!") | ||||
|  | @ -560,6 +561,41 @@ export default class SpecialVisualizations { | |||
|                         }) | ||||
|                     } | ||||
|                 }, | ||||
|                 { | ||||
|                     funcName: "export_as_geojson", | ||||
|                     docs: "Exports the selected feature as GeoJson-file", | ||||
|                     args: [], | ||||
|                     constr: (state, tagSource, args) => { | ||||
|                         const t = Translations.t.general.download; | ||||
| 
 | ||||
|                         return new SubtleButton(Svg.download_ui(), | ||||
|                             new Combine([t.downloadFeatureAsGeojson.SetClass("font-bold text-lg"), | ||||
|                                 t.downloadGeoJsonHelper.SetClass("subtle")]).SetClass("flex flex-col") | ||||
|                         ).onClick(() => { | ||||
|                             console.log("Exporting as Geojson") | ||||
|                             const tags = tagSource.data | ||||
|                             const feature = state.allElements.ContainingFeatures.get(tags.id) | ||||
|                             const matchingLayer = state?.layoutToUse?.getMatchingLayer(tags) | ||||
|                             const title = matchingLayer.title?.GetRenderValue(tags)?.Subs(tags)?.txt ?? "geojson" | ||||
|                             const data = JSON.stringify(feature, null, "  "); | ||||
|                             Utils.offerContentsAsDownloadableFile(data, title + "_mapcomplete_export.geojson", { | ||||
|                                 mimetype: "application/vnd.geo+json" | ||||
|                             }) | ||||
| 
 | ||||
| 
 | ||||
|                         }) | ||||
|                     } | ||||
|                 }, | ||||
|                 { | ||||
|                     funcName: "open_in_iD", | ||||
|                     docs: "Opens the current view in the iD-editor", | ||||
|                     args: [], | ||||
|                     constr: (state, feature ) => { | ||||
|                         return new OpenIdEditor(state, undefined, feature.data.id) | ||||
|                     } | ||||
|                 }, | ||||
|                  | ||||
|                  | ||||
|                 { | ||||
|                     funcName: "clear_location_history", | ||||
|                     docs: "A button to remove the travelled track information from the device", | ||||
|  |  | |||
|  | @ -16,7 +16,7 @@ | |||
|     { | ||||
|       "location": "point", | ||||
|       "icon": { | ||||
|         "render":         "addSmall:#000", | ||||
|         "render": "addSmall:#000", | ||||
|         "mappings": [ | ||||
|           { | ||||
|             "if": "detach=yes", | ||||
|  |  | |||
|  | @ -3,11 +3,16 @@ | |||
|   "description": "The default rendering for a locationInput which snaps onto another object", | ||||
|   "source": { | ||||
|     "osmTags": { | ||||
|       "and": []} | ||||
|       "and": [] | ||||
|     } | ||||
|   }, | ||||
|   "mapRendering": [{ | ||||
|     "location": ["point","centroid"], | ||||
|     "icon": "./assets/svg/crosshair-empty.svg" | ||||
|   }] | ||||
| 
 | ||||
|   "mapRendering": [ | ||||
|     { | ||||
|       "location": [ | ||||
|         "point", | ||||
|         "centroid" | ||||
|       ], | ||||
|       "icon": "./assets/svg/crosshair-empty.svg" | ||||
|     } | ||||
|   ] | ||||
| } | ||||
|  | @ -99,8 +99,7 @@ | |||
|         "osmTags": "building~*", | ||||
|         "maxCacheAge": 0 | ||||
|       }, | ||||
|       "calculatedTags": [ | ||||
|       ], | ||||
|       "calculatedTags": [], | ||||
|       "mapRendering": [ | ||||
|         { | ||||
|           "width": { | ||||
|  | @ -319,7 +318,6 @@ | |||
|         "render": "Service road" | ||||
|       }, | ||||
|       "tagRenderings": [] | ||||
|        | ||||
|     }, | ||||
|     { | ||||
|       "id": "generic_osm_object", | ||||
|  | @ -475,7 +473,10 @@ | |||
|       "description": "Geometry which comes from GRB with tools to import them", | ||||
|       "source": { | ||||
|         "osmTags": { | ||||
|          "and": ["HUISNR~*","man_made!=mast"] | ||||
|           "and": [ | ||||
|             "HUISNR~*", | ||||
|             "man_made!=mast" | ||||
|           ] | ||||
|         }, | ||||
|         "geoJson": "https://betadata.grbosm.site/grb?bbox={x_min},{y_min},{x_max},{y_max}", | ||||
|         "geoJsonZoomLevel": 18, | ||||
|  | @ -508,7 +509,8 @@ | |||
|           "id": "Import-button", | ||||
|           "render": "{import_way_button(OSM-buildings,building=$building;man_made=$man_made; source:geometry:date=$_grb_date; source:geometry:ref=$_grb_ref; addr:street=$addr:street; addr:housenumber=$addr:housenumber; building:min_level=$_building:min_level, Upload this building to OpenStreetMap,,_is_part_of_building=true,1,_moveable=true)}", | ||||
|           "mappings": [ | ||||
|             {"#": "Hide import button if intersection with other objects are detected", | ||||
|             { | ||||
|               "#": "Hide import button if intersection with other objects are detected", | ||||
|               "if": "_intersects_with_other_features~*", | ||||
|               "then": "This GRB building intersects with the following features: {_intersects_with_other_features}.<br/>Fix the overlap and try again" | ||||
|             }, | ||||
|  | @ -555,7 +557,6 @@ | |||
|               "if": "_osm_obj:addr:housenumber~*", | ||||
|               "then": "The overlapping building only has a housenumber known: {_osm_obj:addr:housenumber}" | ||||
|             }, | ||||
|              | ||||
|             { | ||||
|               "if": "_osm_obj:id=", | ||||
|               "then": "No overlapping OpenStreetMap-building found" | ||||
|  |  | |||
|  | @ -198,7 +198,8 @@ | |||
|       "downloadAsPdf": "Download a PDF of the current map", | ||||
|       "downloadAsPdfHelper": "Ideal to print the current map", | ||||
|       "downloadGeojson": "Download visible data as GeoJSON", | ||||
|       "downloadGpx": "Download as GPX-file", | ||||
|       "downloadFeatureAsGpx": "Download as GPX-file", | ||||
|       "downloadFeatureAsGeojson": "Download as GeoJson-file", | ||||
|       "downloadGpxHelper": "A GPX-file can be used with most navigation devices and applications", | ||||
|       "uploadGpx": "Upload your track to OpenStreetMap", | ||||
|       "exporting": "Exporting…", | ||||
|  |  | |||
|  | @ -56,7 +56,7 @@ export default class T { | |||
|      * Returns an empty list if successful | ||||
|      * @constructor | ||||
|      */ | ||||
|     public Run(): ({ testsuite: string, name: string, msg: string } []) { | ||||
|     public Run(): { testsuite: string, name: string, msg: string } [] { | ||||
|         const failures: { testsuite: string, name: string, msg: string } [] = [] | ||||
|         for (const [name, test] of this._tests) { | ||||
|             try { | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue