forked from MapComplete/MapComplete
		
	Merge pull request #1003 from pietervdvn/feature/maproulette
Maproulette tasks
This commit is contained in:
		
						commit
						c81d36fb8f
					
				
					 24 changed files with 1253 additions and 15 deletions
				
			
		| 
						 | 
				
			
			@ -349,11 +349,12 @@ snap_onto_layers | _undefined_ | If a way of the given layer(s) is closeby, will
 | 
			
		|||
max_snap_distance | 5 | The maximum distance that the imported point will be moved to snap onto a way in an already existing layer (in meters). This is previewed to the contributor, similar to the 'add new point'-action of MapComplete
 | 
			
		||||
note_id | _undefined_ | If given, this key will be read. The corresponding note on OSM will be closed, stating 'imported'
 | 
			
		||||
location_picker | photo | Chooses the background for the precise location picker, options are 'map', 'photo' or 'osmbasedmap' or 'none' if the precise input picker should be disabled
 | 
			
		||||
maproulette_id | _undefined_ | If given, the maproulette challenge will be marked as fixed
 | 
			
		||||
 
 | 
			
		||||
 | 
			
		||||
#### Example usage of import_button 
 | 
			
		||||
 | 
			
		||||
 `{import_button(,,Import this data into OpenStreetMap,./assets/svg/addSmall.svg,,5,,photo)}`
 | 
			
		||||
 `{import_button(,,Import this data into OpenStreetMap,./assets/svg/addSmall.svg,,5,,photo,)}`
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -82,6 +82,28 @@ export default class GeoJsonSource implements FeatureSourceForLayer, Tiled {
 | 
			
		|||
        Utils.downloadJsonCached(url, 60 * 60)
 | 
			
		||||
            .then(json => {
 | 
			
		||||
                self.state.setData("loaded")
 | 
			
		||||
                // TODO: move somewhere else, just for testing
 | 
			
		||||
                // Check for maproulette data
 | 
			
		||||
                if (url.startsWith("https://maproulette.org/api/v2/tasks/box/")) {
 | 
			
		||||
                    console.log("MapRoulette data detected")
 | 
			
		||||
                    const data = json;
 | 
			
		||||
                    let maprouletteFeatures: any[] = [];
 | 
			
		||||
                    data.forEach(element => {
 | 
			
		||||
                        maprouletteFeatures.push({
 | 
			
		||||
                            type: "Feature",
 | 
			
		||||
                            geometry: {
 | 
			
		||||
                                type: "Point",
 | 
			
		||||
                                coordinates: [element.point.lng, element.point.lat]
 | 
			
		||||
                            },
 | 
			
		||||
                            properties: {
 | 
			
		||||
                                // Map all properties to the feature
 | 
			
		||||
                                ...element,
 | 
			
		||||
                            }
 | 
			
		||||
                        });
 | 
			
		||||
                    });
 | 
			
		||||
                    json.features = maprouletteFeatures;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (json.features === undefined || json.features === null) {
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										39
									
								
								Logic/Maproulette.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								Logic/Maproulette.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,39 @@
 | 
			
		|||
import Constants from "../Models/Constants";
 | 
			
		||||
 | 
			
		||||
export default class Maproulette {
 | 
			
		||||
  /**
 | 
			
		||||
   * The API endpoint to use
 | 
			
		||||
   */
 | 
			
		||||
  endpoint: string;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * The API key to use for all requests
 | 
			
		||||
   */
 | 
			
		||||
  private apiKey: string;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Creates a new Maproulette instance
 | 
			
		||||
   * @param endpoint The API endpoint to use
 | 
			
		||||
   */
 | 
			
		||||
  constructor(endpoint: string = "https://maproulette.org/api/v2") {
 | 
			
		||||
    this.endpoint = endpoint;
 | 
			
		||||
    this.apiKey = Constants.MaprouletteApiKey;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Close a task
 | 
			
		||||
   * @param taskId The task to close
 | 
			
		||||
   */
 | 
			
		||||
  async closeTask(taskId: number): Promise<void> {
 | 
			
		||||
    const response = await fetch(`${this.endpoint}/task/${taskId}/1`, {
 | 
			
		||||
      method: "PUT",
 | 
			
		||||
      headers: {
 | 
			
		||||
        "Content-Type": "application/json",
 | 
			
		||||
        "apiKey": this.apiKey,
 | 
			
		||||
      },
 | 
			
		||||
    });
 | 
			
		||||
    if (response.status !== 304) {
 | 
			
		||||
      console.log(`Failed to close task: ${response.status}`);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -13,6 +13,7 @@ import ChangeToElementsActor from "../Actors/ChangeToElementsActor";
 | 
			
		|||
import PendingChangesUploader from "../Actors/PendingChangesUploader";
 | 
			
		||||
import * as translators from "../../assets/translators.json"
 | 
			
		||||
import {post} from "jquery";
 | 
			
		||||
import Maproulette from "../Maproulette";
 | 
			
		||||
        
 | 
			
		||||
/**
 | 
			
		||||
 * The part of the state which keeps track of user-related stuff, e.g. the OSM-connection,
 | 
			
		||||
| 
						 | 
				
			
			@ -34,6 +35,11 @@ export default class UserRelatedState extends ElementsState {
 | 
			
		|||
     */
 | 
			
		||||
    public mangroveIdentity: MangroveIdentity;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Maproulette connection
 | 
			
		||||
     */
 | 
			
		||||
    public maprouletteConnection: Maproulette;
 | 
			
		||||
 | 
			
		||||
    public readonly isTranslator : Store<boolean>;
 | 
			
		||||
    
 | 
			
		||||
    public readonly installedUserThemes: Store<string[]>
 | 
			
		||||
| 
						 | 
				
			
			@ -80,6 +86,8 @@ export default class UserRelatedState extends ElementsState {
 | 
			
		|||
            this.osmConnection.GetLongPreference("identity", "mangrove")
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        this.maprouletteConnection = new Maproulette();
 | 
			
		||||
 | 
			
		||||
        if (layoutToUse?.hideFromOverview) {
 | 
			
		||||
            this.osmConnection.isLoggedIn.addCallbackAndRunD(loggedIn => {
 | 
			
		||||
                if (loggedIn) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,6 +7,15 @@ export default class Constants {
 | 
			
		|||
    public static ImgurApiKey = '7070e7167f0a25a'
 | 
			
		||||
    public static readonly mapillary_client_token_v4 = "MLY|4441509239301885|b40ad2d3ea105435bd40c7e76993ae85"
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * API key for Maproulette
 | 
			
		||||
     * 
 | 
			
		||||
     * Currently there is no user-friendly way to get the user's API key.
 | 
			
		||||
     * See https://github.com/maproulette/maproulette2/issues/476 for more information.
 | 
			
		||||
     * Using an empty string however does work for most actions, but will attribute all actions to the Superuser.
 | 
			
		||||
     */
 | 
			
		||||
    public static readonly MaprouletteApiKey = "";
 | 
			
		||||
 | 
			
		||||
    public static defaultOverpassUrls = [
 | 
			
		||||
        // The official instance, 10000 queries per day per project allowed
 | 
			
		||||
        "https://overpass-api.de/api/interpreter",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -550,15 +550,21 @@ export class ImportPointButton extends AbstractImportButton {
 | 
			
		|||
                    name: "note_id",
 | 
			
		||||
                    doc: "If given, this key will be read. The corresponding note on OSM will be closed, stating 'imported'"
 | 
			
		||||
                },
 | 
			
		||||
                {name:"location_picker",
 | 
			
		||||
                {
 | 
			
		||||
                    name:"location_picker",
 | 
			
		||||
                    defaultValue: "photo",
 | 
			
		||||
                doc: "Chooses the background for the precise location picker, options are 'map', 'photo' or 'osmbasedmap' or 'none' if the precise input picker should be disabled"}],
 | 
			
		||||
                    doc: "Chooses the background for the precise location picker, options are 'map', 'photo' or 'osmbasedmap' or 'none' if the precise input picker should be disabled"
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    name: "maproulette_id",
 | 
			
		||||
                    doc: "If given, the maproulette challenge will be marked as fixed"
 | 
			
		||||
                }],
 | 
			
		||||
            { showRemovedTags: false}
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static createConfirmPanelForPoint(
 | 
			
		||||
        args: { max_snap_distance: string, snap_onto_layers: string, icon: string, text: string, newTags: UIEventSource<any>, targetLayer: string, note_id: string },
 | 
			
		||||
        args: { max_snap_distance: string, snap_onto_layers: string, icon: string, text: string, newTags: UIEventSource<any>, targetLayer: string, note_id: string, maproulette_id: string },
 | 
			
		||||
        state: FeaturePipelineState,
 | 
			
		||||
        guiState: DefaultGuiState,
 | 
			
		||||
        originalFeatureTags: UIEventSource<any>,
 | 
			
		||||
| 
						 | 
				
			
			@ -600,6 +606,19 @@ export class ImportPointButton extends AbstractImportButton {
 | 
			
		|||
                originalFeatureTags.data["closed_at"] = new Date().toISOString()
 | 
			
		||||
                originalFeatureTags.ping()
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            let maproulette_id = originalFeatureTags.data[args.maproulette_id];
 | 
			
		||||
            console.log("Checking if we need to mark a maproulette task as fixed (" + maproulette_id + ")")
 | 
			
		||||
            if (maproulette_id !== undefined) {
 | 
			
		||||
                if (state.featureSwitchIsTesting.data){
 | 
			
		||||
                    console.log("Not marking maproulette task " + maproulette_id + " as fixed, because we are in testing mode")
 | 
			
		||||
                } else {
 | 
			
		||||
                    console.log("Marking maproulette task as fixed")
 | 
			
		||||
                    state.maprouletteConnection.closeTask(Number(maproulette_id));
 | 
			
		||||
                    originalFeatureTags.data["mr_taskStatus"] = "Fixed";
 | 
			
		||||
                    originalFeatureTags.ping();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let preciseInputOption = args["location_picker"]
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -42,6 +42,13 @@ export default class TagApplyButton implements AutoAction {
 | 
			
		|||
 | 
			
		||||
    public static generateTagsToApply(spec: string, tagSource: Store<any>): Store<Tag[]> {
 | 
			
		||||
 | 
			
		||||
        // Check whether we need to look up a single value
 | 
			
		||||
 | 
			
		||||
        if (!spec.includes(";") && !spec.includes("=") && spec.includes("$")){
 | 
			
		||||
            // We seem to be dealing with a single value, fetch it
 | 
			
		||||
            spec = tagSource.data[spec.replace("$","")]
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const tgsSpec = spec.split(";").map(spec => {
 | 
			
		||||
            const kv = spec.split("=").map(s => s.trim());
 | 
			
		||||
            if (kv.length != 2) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,4 @@
 | 
			
		|||
import {Store, UIEventSource} from "../Logic/UIEventSource";
 | 
			
		||||
import {Store, Stores, UIEventSource} from "../Logic/UIEventSource";
 | 
			
		||||
import {VariableUiElement} from "./Base/VariableUIElement";
 | 
			
		||||
import LiveQueryHandler from "../Logic/Web/LiveQueryHandler";
 | 
			
		||||
import {ImageCarousel} from "./Image/ImageCarousel";
 | 
			
		||||
| 
						 | 
				
			
			@ -57,8 +57,9 @@ import {SaveButton} from "./Popup/SaveButton";
 | 
			
		|||
import {MapillaryLink} from "./BigComponents/MapillaryLink";
 | 
			
		||||
import {CheckBox} from "./Input/Checkboxes";
 | 
			
		||||
import Slider from "./Input/Slider";
 | 
			
		||||
import {OsmFeature} from "../Models/OsmFeature";
 | 
			
		||||
import List from "./Base/List";
 | 
			
		||||
import StatisticsPanel from "./BigComponents/StatisticsPanel";
 | 
			
		||||
import { OsmFeature } from "../Models/OsmFeature";
 | 
			
		||||
 | 
			
		||||
export interface SpecialVisualization {
 | 
			
		||||
    funcName: string,
 | 
			
		||||
| 
						 | 
				
			
			@ -1100,6 +1101,39 @@ export default class SpecialVisualizations {
 | 
			
		|||
                },
 | 
			
		||||
                new NearbyImageVis(),
 | 
			
		||||
                new MapillaryLinkVis(),
 | 
			
		||||
                {
 | 
			
		||||
                    funcName: "maproulette_task",
 | 
			
		||||
                    args: [],
 | 
			
		||||
                    constr(state, tagSource, argument, guistate) {
 | 
			
		||||
                        let parentId = tagSource.data.mr_challengeId;
 | 
			
		||||
                        let challenge = Stores.FromPromise(Utils.downloadJsonCached(`https://maproulette.org/api/v2/challenge/${parentId}`,24*60*60*1000));
 | 
			
		||||
 | 
			
		||||
                        let details = new VariableUiElement( challenge.map(challenge => {
 | 
			
		||||
                            let listItems: BaseUIElement[] = [];
 | 
			
		||||
                            let title: BaseUIElement;
 | 
			
		||||
                            
 | 
			
		||||
                            if (challenge?.name) {
 | 
			
		||||
                                title = new Title(challenge.name);
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                            if (challenge?.description) {
 | 
			
		||||
                                listItems.push(new FixedUiElement(challenge.description));
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                            if (challenge?.instruction) {
 | 
			
		||||
                                listItems.push(new FixedUiElement(challenge.instruction));
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                            if(listItems.length === 0) {
 | 
			
		||||
                                return undefined;
 | 
			
		||||
                            } else {
 | 
			
		||||
                                return [title, new List(listItems)];
 | 
			
		||||
                            }
 | 
			
		||||
                        }))
 | 
			
		||||
                        return details; 
 | 
			
		||||
                    },
 | 
			
		||||
                    docs: "Show details of a MapRoulette task"
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    funcName: "statistics",
 | 
			
		||||
                    docs: "Show general statistics about the elements currently in view. Intended to use on the `current_view`-layer",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										12
									
								
								assets/layers/maproulette/license_info.json
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								assets/layers/maproulette/license_info.json
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,12 @@
 | 
			
		|||
[
 | 
			
		||||
  {
 | 
			
		||||
    "path": "logomark.svg",
 | 
			
		||||
    "license": "MIT",
 | 
			
		||||
    "authors": [
 | 
			
		||||
      "MapRoulette"
 | 
			
		||||
    ],
 | 
			
		||||
    "sources": [
 | 
			
		||||
      "https://github.com/maproulette/docs/blob/master/src/assets/svg/logo.svg"
 | 
			
		||||
    ]
 | 
			
		||||
  }
 | 
			
		||||
]
 | 
			
		||||
							
								
								
									
										77
									
								
								assets/layers/maproulette/logomark.svg
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								assets/layers/maproulette/logomark.svg
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| 
		 After Width: | Height: | Size: 8.5 KiB  | 
							
								
								
									
										225
									
								
								assets/layers/maproulette/maproulette.json
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										225
									
								
								assets/layers/maproulette/maproulette.json
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,225 @@
 | 
			
		|||
{
 | 
			
		||||
  "id": "maproulette",
 | 
			
		||||
  "source": {
 | 
			
		||||
    "geoJson": "https://maproulette.org/api/v2/tasks/box/{x_min}/{y_min}/{x_max}/{y_max}",
 | 
			
		||||
    "geoJsonZoomLevel": 16,
 | 
			
		||||
    "osmTags": "id~*"
 | 
			
		||||
  },
 | 
			
		||||
  "mapRendering": [
 | 
			
		||||
    {
 | 
			
		||||
      "location": [
 | 
			
		||||
        "point",
 | 
			
		||||
        "centroid"
 | 
			
		||||
      ],
 | 
			
		||||
      "icon": {
 | 
			
		||||
        "render": "./assets/layers/maproulette/logomark.svg",
 | 
			
		||||
        "mappings": [
 | 
			
		||||
          {
 | 
			
		||||
            "if": "status=0",
 | 
			
		||||
            "then": "pin:#959DFF"
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            "if": "status=1",
 | 
			
		||||
            "then": "pin:#65D2DA"
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            "if": "status=2",
 | 
			
		||||
            "then": "pin:#F7BB59"
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            "if": "status=3",
 | 
			
		||||
            "then": "pin:#F7BB59"
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            "if": "status=4",
 | 
			
		||||
            "then": "pin:#737373"
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            "if": "status=5",
 | 
			
		||||
            "then": "pin:#CCB186"
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            "if": "status=6",
 | 
			
		||||
            "then": "pin:#FF5E63"
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            "if": "status=9",
 | 
			
		||||
            "then": "pin:#FF349C"
 | 
			
		||||
          }
 | 
			
		||||
        ]
 | 
			
		||||
      },
 | 
			
		||||
      "iconSize": "40,40,center"
 | 
			
		||||
    }
 | 
			
		||||
  ],
 | 
			
		||||
  "tagRenderings": [
 | 
			
		||||
    {
 | 
			
		||||
      "id": "status",
 | 
			
		||||
      "render": "Current status: {status}",
 | 
			
		||||
      "mappings": [
 | 
			
		||||
        {
 | 
			
		||||
          "if": "status=0",
 | 
			
		||||
          "then": {
 | 
			
		||||
            "en": "Task is created"
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "if": "status=1",
 | 
			
		||||
          "then": {
 | 
			
		||||
            "en": "Task is fixed"
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "if": "status=2",
 | 
			
		||||
          "then": {
 | 
			
		||||
            "en": "Task is a false positive"
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "if": "status=3",
 | 
			
		||||
          "then": {
 | 
			
		||||
            "en": "Task is skipped"
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "if": "status=4",
 | 
			
		||||
          "then": {
 | 
			
		||||
            "en": "Task is deleted"
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "if": "status=5",
 | 
			
		||||
          "then": {
 | 
			
		||||
            "en": "Task is already fixed"
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "if": "status=6",
 | 
			
		||||
          "then": {
 | 
			
		||||
            "en": "Task is marked as too hard"
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "if": "status=9",
 | 
			
		||||
          "then": {
 | 
			
		||||
            "en": "Task is disabled"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      ]
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "id": "blurb",
 | 
			
		||||
      "condition": "blurb~*",
 | 
			
		||||
      "render": "{blurb}"
 | 
			
		||||
    }
 | 
			
		||||
  ],
 | 
			
		||||
  "description": {
 | 
			
		||||
    "en": "Layer showing all tasks in MapRoulette"
 | 
			
		||||
  },
 | 
			
		||||
  "minzoom": 15,
 | 
			
		||||
  "name": {
 | 
			
		||||
    "en": "MapRoulette Tasks"
 | 
			
		||||
  },
 | 
			
		||||
  "title": {
 | 
			
		||||
    "render": {
 | 
			
		||||
      "en": "MapRoulette Item: {parentName}"
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  "titleIcons": [
 | 
			
		||||
    {
 | 
			
		||||
      "id": "maproulette",
 | 
			
		||||
      "render": "<a href='https://maproulette.org/challenge/{parentId}/task/{id}' target='_blank'><img src='./assets/layers/maproulette/logomark.svg'/></a>"
 | 
			
		||||
    }
 | 
			
		||||
  ],
 | 
			
		||||
  "filter": [
 | 
			
		||||
    {
 | 
			
		||||
      "id": "status",
 | 
			
		||||
      "options": [
 | 
			
		||||
        {
 | 
			
		||||
          "question": {
 | 
			
		||||
            "en": "Show tasks with all statuses"
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "question": {
 | 
			
		||||
            "en": "Show tasks that are created"
 | 
			
		||||
          },
 | 
			
		||||
          "osmTags": "status=0"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "question": {
 | 
			
		||||
            "en": "Show tasks that are fixed"
 | 
			
		||||
          },
 | 
			
		||||
          "osmTags": "status=1"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "question": {
 | 
			
		||||
            "en": "Show tasks that are false positives"
 | 
			
		||||
          },
 | 
			
		||||
          "osmTags": "status=2"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "question": {
 | 
			
		||||
            "en": "Show tasks that are skipped"
 | 
			
		||||
          },
 | 
			
		||||
          "osmTags": "status=3"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "question": {
 | 
			
		||||
            "en": "Show tasks that are deleted"
 | 
			
		||||
          },
 | 
			
		||||
          "osmTags": "status=4"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "question": {
 | 
			
		||||
            "en": "Show tasks that are already fixed"
 | 
			
		||||
          },
 | 
			
		||||
          "osmTags": "status=5"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "question": {
 | 
			
		||||
            "en": "Show tasks that are marked as too hard"
 | 
			
		||||
          },
 | 
			
		||||
          "osmTags": "status=6"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "question": {
 | 
			
		||||
            "en": "Show tasks that are disabled"
 | 
			
		||||
          },
 | 
			
		||||
          "osmTags": "status=9"
 | 
			
		||||
        }
 | 
			
		||||
      ]
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "id": "parent-name",
 | 
			
		||||
      "options": [
 | 
			
		||||
        {
 | 
			
		||||
          "osmTags": "parentName~i~.*{search}.*",
 | 
			
		||||
          "fields": [
 | 
			
		||||
            {
 | 
			
		||||
              "name": "search"
 | 
			
		||||
            }
 | 
			
		||||
          ],
 | 
			
		||||
          "question": {
 | 
			
		||||
            "en": "Challenge name contains {search}"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      ]
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "id": "parent-id",
 | 
			
		||||
      "options": [
 | 
			
		||||
        {
 | 
			
		||||
          "osmTags": "parentId={search}",
 | 
			
		||||
          "fields": [
 | 
			
		||||
            {
 | 
			
		||||
              "name": "search"
 | 
			
		||||
            }
 | 
			
		||||
          ],
 | 
			
		||||
          "question": {
 | 
			
		||||
            "en": "Challenge ID matches {search}"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      ]
 | 
			
		||||
    }
 | 
			
		||||
  ]
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										194
									
								
								assets/layers/maproulette_challenge/maproulette_challenge.json
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										194
									
								
								assets/layers/maproulette_challenge/maproulette_challenge.json
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,194 @@
 | 
			
		|||
{
 | 
			
		||||
  "id": "maproulette_challenge",
 | 
			
		||||
  "name": null,
 | 
			
		||||
  "description": {
 | 
			
		||||
    "en": "Layer showing tasks of a MapRoulette challenge"
 | 
			
		||||
  },
 | 
			
		||||
  "source": {
 | 
			
		||||
    "osmTags": "id~*",
 | 
			
		||||
    "geoJson": "https://maproulette.org/api/v2/challenge/view/27971",
 | 
			
		||||
    "isOsmCache": false
 | 
			
		||||
  },
 | 
			
		||||
  "title": {
 | 
			
		||||
    "render": {
 | 
			
		||||
      "en": "Item in MapRoulette"
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  "titleIcons": [
 | 
			
		||||
    {
 | 
			
		||||
      "id": "maproulette",
 | 
			
		||||
      "render": "<a href='https://maproulette.org/challenge/{mr_challengeId}/task/{mr_taskId}' target='_blank'><img src='./assets/layers/maproulette/logomark.svg'/></a>"
 | 
			
		||||
    }
 | 
			
		||||
  ],
 | 
			
		||||
  "mapRendering": [
 | 
			
		||||
    {
 | 
			
		||||
      "location": [
 | 
			
		||||
        "point",
 | 
			
		||||
        "centroid"
 | 
			
		||||
      ],
 | 
			
		||||
      "icon": {
 | 
			
		||||
        "render": "./assets/layers/maproulette/logomark.svg",
 | 
			
		||||
        "mappings": [
 | 
			
		||||
          {
 | 
			
		||||
            "if": "mr_taskStatus=Created",
 | 
			
		||||
            "then": "pin:#959DFF"
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            "if": "mr_taskStatus=Fixed",
 | 
			
		||||
            "then": "pin:#65D2DA"
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            "if": "mr_taskStatus=False positive",
 | 
			
		||||
            "then": "pin:#F7BB59"
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            "if": "mr_taskStatus=Skipped",
 | 
			
		||||
            "then": "pin:#F7BB59"
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            "if": "mr_taskStatus=Deleted",
 | 
			
		||||
            "then": "pin:#737373"
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            "if": "mr_taskStatus=Already fixed",
 | 
			
		||||
            "then": "pin:#CCB186"
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            "if": "mr_taskStatus=Too hard",
 | 
			
		||||
            "then": "pin:#FF5E63"
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            "if": "mr_taskStatus=Disabled",
 | 
			
		||||
            "then": "pin:#FF349C"
 | 
			
		||||
          }
 | 
			
		||||
        ]
 | 
			
		||||
      },
 | 
			
		||||
      "iconSize": "40,40,bottom"
 | 
			
		||||
    }
 | 
			
		||||
  ],
 | 
			
		||||
  "tagRenderings": [
 | 
			
		||||
    {
 | 
			
		||||
      "id": "details",
 | 
			
		||||
      "render": "{maproulette_task()}"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "id": "status",
 | 
			
		||||
      "render": "Current status: {status}",
 | 
			
		||||
      "mappings": [
 | 
			
		||||
        {
 | 
			
		||||
          "if": "mr_taskStatus=Created",
 | 
			
		||||
          "then": {
 | 
			
		||||
            "en": "Task is created"
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "if": "mr_taskStatus=Fixed",
 | 
			
		||||
          "then": {
 | 
			
		||||
            "en": "Task is fixed"
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "if": "mr_taskStatus=False positive",
 | 
			
		||||
          "then": {
 | 
			
		||||
            "en": "Task is a false positive"
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "if": "mr_taskStatus=Skipped",
 | 
			
		||||
          "then": {
 | 
			
		||||
            "en": "Task is skipped"
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "if": "mr_taskStatus=Deleted",
 | 
			
		||||
          "then": {
 | 
			
		||||
            "en": "Task is deleted"
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "if": "mr_taskStatus=Already fixed",
 | 
			
		||||
          "then": {
 | 
			
		||||
            "en": "Task is already fixed"
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "if": "mr_taskStatus=Too hard",
 | 
			
		||||
          "then": {
 | 
			
		||||
            "en": "Task is marked as too hard"
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "if": "mr_taskStatus=Disabled",
 | 
			
		||||
          "then": {
 | 
			
		||||
            "en": "Task is disabled"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      ]
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "id": "blurb",
 | 
			
		||||
      "condition": "blurb~*",
 | 
			
		||||
      "render": "{blurb}"
 | 
			
		||||
    }
 | 
			
		||||
  ],
 | 
			
		||||
  "filter": [
 | 
			
		||||
    {
 | 
			
		||||
      "id": "status",
 | 
			
		||||
      "options": [
 | 
			
		||||
        {
 | 
			
		||||
          "question": {
 | 
			
		||||
            "en": "Show tasks with all statuses"
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "question": {
 | 
			
		||||
            "en": "Show tasks that are created"
 | 
			
		||||
          },
 | 
			
		||||
          "osmTags": "mr_taskStatus=Created"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "question": {
 | 
			
		||||
            "en": "Show tasks that are fixed"
 | 
			
		||||
          },
 | 
			
		||||
          "osmTags": "mr_taskStatus=Fixed"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "question": {
 | 
			
		||||
            "en": "Show tasks that are false positives"
 | 
			
		||||
          },
 | 
			
		||||
          "osmTags": "mr_taskStatus=False positive"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "question": {
 | 
			
		||||
            "en": "Show tasks that are skipped"
 | 
			
		||||
          },
 | 
			
		||||
          "osmTags": "mr_taskStatus=Skipped"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "question": {
 | 
			
		||||
            "en": "Show tasks that are deleted"
 | 
			
		||||
          },
 | 
			
		||||
          "osmTags": "mr_taskStatus=Deleted"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "question": {
 | 
			
		||||
            "en": "Show tasks that are already fixed"
 | 
			
		||||
          },
 | 
			
		||||
          "osmTags": "mr_taskStatus=Already fixed"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "question": {
 | 
			
		||||
            "en": "Show tasks that are marked as too hard"
 | 
			
		||||
          },
 | 
			
		||||
          "osmTags": "mr_taskStatus=Too hard"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "question": {
 | 
			
		||||
            "en": "Show tasks that are disabled"
 | 
			
		||||
          },
 | 
			
		||||
          "osmTags": "mr_taskStatus=Disabled"
 | 
			
		||||
        }
 | 
			
		||||
      ]
 | 
			
		||||
    }
 | 
			
		||||
  ]
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -303,7 +303,7 @@
 | 
			
		|||
  },
 | 
			
		||||
  "allowMove": {
 | 
			
		||||
    "enableRelocation": false,
 | 
			
		||||
    "enableImproveAccuraccy": true
 | 
			
		||||
    "enableImproveAccuracy": true
 | 
			
		||||
  },
 | 
			
		||||
  "mapRendering": [
 | 
			
		||||
    {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -246,6 +246,10 @@
 | 
			
		|||
                "if": "theme=mapcomplete-changes",
 | 
			
		||||
                "then": "./assets/svg/logo.svg"
 | 
			
		||||
              },
 | 
			
		||||
              {
 | 
			
		||||
                "if": "theme=maproulette",
 | 
			
		||||
                "then": "./assets/layers/maproulette/logomark.svg"
 | 
			
		||||
              },
 | 
			
		||||
              {
 | 
			
		||||
                "if": "theme=maps",
 | 
			
		||||
                "then": "./assets/themes/maps/logo.svg"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										19
									
								
								assets/themes/maproulette/maproulette.json
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								assets/themes/maproulette/maproulette.json
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,19 @@
 | 
			
		|||
{
 | 
			
		||||
  "id": "maproulette",
 | 
			
		||||
  "title": {
 | 
			
		||||
    "en": "MapRoulette Tasks"
 | 
			
		||||
  },
 | 
			
		||||
  "description": {
 | 
			
		||||
    "en": "Theme showing MapRoulette tasks, allowing you to search, filter and fix them."
 | 
			
		||||
  },
 | 
			
		||||
  "version": "1.0.0",
 | 
			
		||||
  "hideFromOverview": true,
 | 
			
		||||
  "icon": "./assets/layers/maproulette/logomark.svg",
 | 
			
		||||
  "maintainer": "",
 | 
			
		||||
  "startLat": 0,
 | 
			
		||||
  "startLon": 0,
 | 
			
		||||
  "startZoom": 4,
 | 
			
		||||
  "layers": [
 | 
			
		||||
    "maproulette"
 | 
			
		||||
  ]
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -366,6 +366,53 @@
 | 
			
		|||
          }
 | 
			
		||||
        ]
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "builtin": "maproulette_challenge",
 | 
			
		||||
      "override": {
 | 
			
		||||
        "source": {
 | 
			
		||||
          "geoJson": "https://maproulette.org/api/v2/challenge/view/28012"
 | 
			
		||||
        },
 | 
			
		||||
        "calculatedTags": [
 | 
			
		||||
          "_closest_osm_hotel=feat.closest('hotel')?.properties?.id",
 | 
			
		||||
          "_closest_osm_hotel_distance=feat.distanceTo(feat.properties._closest_osm_hotel)",
 | 
			
		||||
          "_has_closeby_feature=Number(feat.properties._closest_osm_hotel_distance) < 50 ? 'yes' : 'no'"
 | 
			
		||||
        ],
 | 
			
		||||
        "+tagRenderings": [
 | 
			
		||||
          {
 | 
			
		||||
            "id": "import-button",
 | 
			
		||||
            "condition": "_has_closeby_feature=no",
 | 
			
		||||
            "render": {
 | 
			
		||||
              "special": {
 | 
			
		||||
                "type": "import_button",
 | 
			
		||||
                "targetLayer": "hotel",
 | 
			
		||||
                "tags": "tags",
 | 
			
		||||
                "text": {
 | 
			
		||||
                  "en": "Import"
 | 
			
		||||
                },
 | 
			
		||||
                "icon": "./assets/svg/addSmall.svg",
 | 
			
		||||
                "location_picker": "photo",
 | 
			
		||||
                "maproulette_id": "mr_taskId"
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            "id": "tag-apply-button",
 | 
			
		||||
            "condition": "_has_closeby_feature=yes",
 | 
			
		||||
            "render": {
 | 
			
		||||
              "special": {
 | 
			
		||||
                "type": "tag_apply",
 | 
			
		||||
                "tags_to_apply": "$tags",
 | 
			
		||||
                "message": {
 | 
			
		||||
                  "en": "Add all the suggested tags"
 | 
			
		||||
                },
 | 
			
		||||
                "image": "./assets/svg/addSmall.svg",
 | 
			
		||||
                "id_of_object_to_apply_this_one": "_closest_osm_hotel"
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        ]
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  ],
 | 
			
		||||
  "overrideAll": {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -51,6 +51,22 @@
 | 
			
		|||
      "tagRenderings": [
 | 
			
		||||
        "all_tags"
 | 
			
		||||
      ]
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "builtin": "maproulette_challenge",
 | 
			
		||||
      "override": {
 | 
			
		||||
        "calculatedTags": [
 | 
			
		||||
          "_closest_osm_street_lamp=feat.closest('street_lamps')?.properties?.id",
 | 
			
		||||
          "_closest_osm_street_lamp_distance=feat.distanceTo(feat.properties._closest_osm_street_lamp)",
 | 
			
		||||
          "_has_closeby_feature=Number(feat.properties._closest_osm_street_lamp_distance) < 5 ? 'yes' : 'no'"
 | 
			
		||||
        ],
 | 
			
		||||
        "tagRenderings+": [
 | 
			
		||||
          {
 | 
			
		||||
            "id": "import",
 | 
			
		||||
            "render": "{import_button(street_lamps,tags,Import,./assets/svg/addSmall.svg,,,,photo,mr_taskId)}"
 | 
			
		||||
          }
 | 
			
		||||
        ]
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  ],
 | 
			
		||||
  "hideFromOverview": true
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4464,6 +4464,159 @@
 | 
			
		|||
            "render": "Map"
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    "maproulette": {
 | 
			
		||||
        "description": "Layer showing all tasks in MapRoulette",
 | 
			
		||||
        "filter": {
 | 
			
		||||
            "0": {
 | 
			
		||||
                "options": {
 | 
			
		||||
                    "0": {
 | 
			
		||||
                        "question": "Show tasks with all statuses"
 | 
			
		||||
                    },
 | 
			
		||||
                    "1": {
 | 
			
		||||
                        "question": "Show tasks that are created"
 | 
			
		||||
                    },
 | 
			
		||||
                    "2": {
 | 
			
		||||
                        "question": "Show tasks that are fixed"
 | 
			
		||||
                    },
 | 
			
		||||
                    "3": {
 | 
			
		||||
                        "question": "Show tasks that are false positives"
 | 
			
		||||
                    },
 | 
			
		||||
                    "4": {
 | 
			
		||||
                        "question": "Show tasks that are skipped"
 | 
			
		||||
                    },
 | 
			
		||||
                    "5": {
 | 
			
		||||
                        "question": "Show tasks that are deleted"
 | 
			
		||||
                    },
 | 
			
		||||
                    "6": {
 | 
			
		||||
                        "question": "Show tasks that are already fixed"
 | 
			
		||||
                    },
 | 
			
		||||
                    "7": {
 | 
			
		||||
                        "question": "Show tasks that are marked as too hard"
 | 
			
		||||
                    },
 | 
			
		||||
                    "8": {
 | 
			
		||||
                        "question": "Show tasks that are disabled"
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            "1": {
 | 
			
		||||
                "options": {
 | 
			
		||||
                    "0": {
 | 
			
		||||
                        "question": "Challenge name contains {search}"
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            "2": {
 | 
			
		||||
                "options": {
 | 
			
		||||
                    "0": {
 | 
			
		||||
                        "question": "Challenge ID matches {search}"
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "name": "MapRoulette Tasks",
 | 
			
		||||
        "tagRenderings": {
 | 
			
		||||
            "status": {
 | 
			
		||||
                "mappings": {
 | 
			
		||||
                    "0": {
 | 
			
		||||
                        "then": "Task is created"
 | 
			
		||||
                    },
 | 
			
		||||
                    "1": {
 | 
			
		||||
                        "then": "Task is fixed"
 | 
			
		||||
                    },
 | 
			
		||||
                    "2": {
 | 
			
		||||
                        "then": "Task is a false positive"
 | 
			
		||||
                    },
 | 
			
		||||
                    "3": {
 | 
			
		||||
                        "then": "Task is skipped"
 | 
			
		||||
                    },
 | 
			
		||||
                    "4": {
 | 
			
		||||
                        "then": "Task is deleted"
 | 
			
		||||
                    },
 | 
			
		||||
                    "5": {
 | 
			
		||||
                        "then": "Task is already fixed"
 | 
			
		||||
                    },
 | 
			
		||||
                    "6": {
 | 
			
		||||
                        "then": "Task is marked as too hard"
 | 
			
		||||
                    },
 | 
			
		||||
                    "7": {
 | 
			
		||||
                        "then": "Task is disabled"
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "title": {
 | 
			
		||||
            "render": "MapRoulette Item: {parentName}"
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    "maproulette_challenge": {
 | 
			
		||||
        "description": "Layer showing tasks of a MapRoulette challenge",
 | 
			
		||||
        "filter": {
 | 
			
		||||
            "0": {
 | 
			
		||||
                "options": {
 | 
			
		||||
                    "0": {
 | 
			
		||||
                        "question": "Show tasks with all statuses"
 | 
			
		||||
                    },
 | 
			
		||||
                    "1": {
 | 
			
		||||
                        "question": "Show tasks that are created"
 | 
			
		||||
                    },
 | 
			
		||||
                    "2": {
 | 
			
		||||
                        "question": "Show tasks that are fixed"
 | 
			
		||||
                    },
 | 
			
		||||
                    "3": {
 | 
			
		||||
                        "question": "Show tasks that are false positives"
 | 
			
		||||
                    },
 | 
			
		||||
                    "4": {
 | 
			
		||||
                        "question": "Show tasks that are skipped"
 | 
			
		||||
                    },
 | 
			
		||||
                    "5": {
 | 
			
		||||
                        "question": "Show tasks that are deleted"
 | 
			
		||||
                    },
 | 
			
		||||
                    "6": {
 | 
			
		||||
                        "question": "Show tasks that are already fixed"
 | 
			
		||||
                    },
 | 
			
		||||
                    "7": {
 | 
			
		||||
                        "question": "Show tasks that are marked as too hard"
 | 
			
		||||
                    },
 | 
			
		||||
                    "8": {
 | 
			
		||||
                        "question": "Show tasks that are disabled"
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "tagRenderings": {
 | 
			
		||||
            "status": {
 | 
			
		||||
                "mappings": {
 | 
			
		||||
                    "0": {
 | 
			
		||||
                        "then": "Task is created"
 | 
			
		||||
                    },
 | 
			
		||||
                    "1": {
 | 
			
		||||
                        "then": "Task is fixed"
 | 
			
		||||
                    },
 | 
			
		||||
                    "2": {
 | 
			
		||||
                        "then": "Task is a false positive"
 | 
			
		||||
                    },
 | 
			
		||||
                    "3": {
 | 
			
		||||
                        "then": "Task is skipped"
 | 
			
		||||
                    },
 | 
			
		||||
                    "4": {
 | 
			
		||||
                        "then": "Task is deleted"
 | 
			
		||||
                    },
 | 
			
		||||
                    "5": {
 | 
			
		||||
                        "then": "Task is already fixed"
 | 
			
		||||
                    },
 | 
			
		||||
                    "6": {
 | 
			
		||||
                        "then": "Task is marked as too hard"
 | 
			
		||||
                    },
 | 
			
		||||
                    "7": {
 | 
			
		||||
                        "then": "Task is disabled"
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "title": {
 | 
			
		||||
            "render": "Item in MapRoulette"
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    "maxspeed": {
 | 
			
		||||
        "description": "Shows the allowed speed for every road",
 | 
			
		||||
        "name": "Maxspeed",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -725,6 +725,10 @@
 | 
			
		|||
        "shortDescription": "Shows changes made by MapComplete",
 | 
			
		||||
        "title": "Changes made with MapComplete"
 | 
			
		||||
    },
 | 
			
		||||
    "maproulette": {
 | 
			
		||||
        "description": "Theme showing MapRoulette tasks, allowing you to search, filter and fix them.",
 | 
			
		||||
        "title": "MapRoulette Tasks"
 | 
			
		||||
    },
 | 
			
		||||
    "maps": {
 | 
			
		||||
        "description": "On this map you can find all maps OpenStreetMap knows - typically a big map on an information board showing the area, city or region, e.g. a tourist map on the back of a billboard, a map of a nature reserve, a map of cycling networks in the region, ...) <br/><br/>If a map is missing, you can easily map this map on OpenStreetMap.",
 | 
			
		||||
        "shortDescription": "This theme shows all (touristic) maps that OpenStreetMap knows of",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										23
									
								
								package-lock.json
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										23
									
								
								package-lock.json
									
										
									
										generated
									
									
									
								
							| 
						 | 
				
			
			@ -18,6 +18,7 @@
 | 
			
		|||
        "@turf/length": "^6.5.0",
 | 
			
		||||
        "@turf/turf": "^6.5.0",
 | 
			
		||||
        "@types/chai": "^4.3.0",
 | 
			
		||||
        "@types/geojson": "^7946.0.10",
 | 
			
		||||
        "@types/jquery": "^3.5.5",
 | 
			
		||||
        "@types/leaflet-markercluster": "^1.0.3",
 | 
			
		||||
        "@types/leaflet-providers": "^1.2.0",
 | 
			
		||||
| 
						 | 
				
			
			@ -3224,9 +3225,9 @@
 | 
			
		|||
      "integrity": "sha512-/ceqdqeRraGolFTcfoXNiqjyQhZzbINDngeoAq9GoHa8PPK1yNzTaxWjA6BFWp5Ua9JpXEMSS4s5i9tS0hOJtw=="
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@types/geojson": {
 | 
			
		||||
      "version": "7946.0.8",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.8.tgz",
 | 
			
		||||
      "integrity": "sha512-1rkryxURpr6aWP7R786/UQOkJ3PcpQiWkAXBmdWc7ryFWqN6a4xfK7BtjXvFBKO9LjQ+MWQSWxYeZX1OApnArA=="
 | 
			
		||||
      "version": "7946.0.10",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.10.tgz",
 | 
			
		||||
      "integrity": "sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA=="
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@types/jquery": {
 | 
			
		||||
      "version": "3.5.5",
 | 
			
		||||
| 
						 | 
				
			
			@ -7176,6 +7177,11 @@
 | 
			
		|||
        "rbush": "^3.0.1"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/geojson-rbush/node_modules/@types/geojson": {
 | 
			
		||||
      "version": "7946.0.8",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.8.tgz",
 | 
			
		||||
      "integrity": "sha512-1rkryxURpr6aWP7R786/UQOkJ3PcpQiWkAXBmdWc7ryFWqN6a4xfK7BtjXvFBKO9LjQ+MWQSWxYeZX1OApnArA=="
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/geojson-rbush/node_modules/quickselect": {
 | 
			
		||||
      "version": "2.0.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz",
 | 
			
		||||
| 
						 | 
				
			
			@ -19255,9 +19261,9 @@
 | 
			
		|||
      "integrity": "sha512-/ceqdqeRraGolFTcfoXNiqjyQhZzbINDngeoAq9GoHa8PPK1yNzTaxWjA6BFWp5Ua9JpXEMSS4s5i9tS0hOJtw=="
 | 
			
		||||
    },
 | 
			
		||||
    "@types/geojson": {
 | 
			
		||||
      "version": "7946.0.8",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.8.tgz",
 | 
			
		||||
      "integrity": "sha512-1rkryxURpr6aWP7R786/UQOkJ3PcpQiWkAXBmdWc7ryFWqN6a4xfK7BtjXvFBKO9LjQ+MWQSWxYeZX1OApnArA=="
 | 
			
		||||
      "version": "7946.0.10",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.10.tgz",
 | 
			
		||||
      "integrity": "sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA=="
 | 
			
		||||
    },
 | 
			
		||||
    "@types/jquery": {
 | 
			
		||||
      "version": "3.5.5",
 | 
			
		||||
| 
						 | 
				
			
			@ -22428,6 +22434,11 @@
 | 
			
		|||
        "rbush": "^3.0.1"
 | 
			
		||||
      },
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "@types/geojson": {
 | 
			
		||||
          "version": "7946.0.8",
 | 
			
		||||
          "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.8.tgz",
 | 
			
		||||
          "integrity": "sha512-1rkryxURpr6aWP7R786/UQOkJ3PcpQiWkAXBmdWc7ryFWqN6a4xfK7BtjXvFBKO9LjQ+MWQSWxYeZX1OApnArA=="
 | 
			
		||||
        },
 | 
			
		||||
        "quickselect": {
 | 
			
		||||
          "version": "2.0.0",
 | 
			
		||||
          "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -77,6 +77,7 @@
 | 
			
		|||
    "@turf/length": "^6.5.0",
 | 
			
		||||
    "@turf/turf": "^6.5.0",
 | 
			
		||||
    "@types/chai": "^4.3.0",
 | 
			
		||||
    "@types/geojson": "^7946.0.10",
 | 
			
		||||
    "@types/jquery": "^3.5.5",
 | 
			
		||||
    "@types/leaflet-markercluster": "^1.0.3",
 | 
			
		||||
    "@types/leaflet-providers": "^1.2.0",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										102
									
								
								scripts/onwheels/constants.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								scripts/onwheels/constants.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,102 @@
 | 
			
		|||
/**
 | 
			
		||||
 * Class containing all constants and tables used in the script
 | 
			
		||||
 * 
 | 
			
		||||
 * @class Constants
 | 
			
		||||
 */
 | 
			
		||||
export default class Constants {
 | 
			
		||||
  /**
 | 
			
		||||
   * Table used to determine tags for the category
 | 
			
		||||
   *
 | 
			
		||||
   * Keys are the original category names,
 | 
			
		||||
   * values are an object containing the tags
 | 
			
		||||
   */
 | 
			
		||||
  public static categories = {
 | 
			
		||||
    restaurant: {
 | 
			
		||||
      amenity: "restaurant",
 | 
			
		||||
    },
 | 
			
		||||
    parking: {
 | 
			
		||||
      amenity: "parking",
 | 
			
		||||
    },
 | 
			
		||||
    hotel: {
 | 
			
		||||
      tourism: "hotel",
 | 
			
		||||
    },
 | 
			
		||||
    wc: {
 | 
			
		||||
      amenity: "toilets",
 | 
			
		||||
    },
 | 
			
		||||
    winkel: {
 | 
			
		||||
      shop: "yes",
 | 
			
		||||
    },
 | 
			
		||||
    apotheek: {
 | 
			
		||||
      amenity: "pharmacy",
 | 
			
		||||
      healthcare: "pharmacy",
 | 
			
		||||
    },
 | 
			
		||||
    ziekenhuis: {
 | 
			
		||||
      amenity: "hospital",
 | 
			
		||||
      healthcare: "hospital",
 | 
			
		||||
    },
 | 
			
		||||
    bezienswaardigheid: {
 | 
			
		||||
      tourism: "attraction",
 | 
			
		||||
    },
 | 
			
		||||
    ontspanning: {
 | 
			
		||||
      fixme: "Needs proper tags",
 | 
			
		||||
    },
 | 
			
		||||
    cafe: {
 | 
			
		||||
      amenity: "cafe",
 | 
			
		||||
    },
 | 
			
		||||
    dienst: {
 | 
			
		||||
      fixme: "Needs proper tags",
 | 
			
		||||
    },
 | 
			
		||||
    bank: {
 | 
			
		||||
      amenity: "bank",
 | 
			
		||||
    },
 | 
			
		||||
    gas: {
 | 
			
		||||
      amenity: "fuel",
 | 
			
		||||
    },
 | 
			
		||||
    medical: {
 | 
			
		||||
      fixme: "Needs proper tags",
 | 
			
		||||
    },
 | 
			
		||||
    obstacle: {
 | 
			
		||||
      fixme: "Needs proper tags",
 | 
			
		||||
    },
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Table used to rename original Onwheels properties to their corresponding OSM properties
 | 
			
		||||
   *
 | 
			
		||||
   * Keys are the original Onwheels properties, values are the corresponding OSM properties
 | 
			
		||||
   */
 | 
			
		||||
  public static names = {
 | 
			
		||||
    ID: "id",
 | 
			
		||||
    Naam: "name",
 | 
			
		||||
    Straat: "addr:street",
 | 
			
		||||
    Nummer: "addr:housenumber",
 | 
			
		||||
    Postcode: "addr:postcode",
 | 
			
		||||
    Plaats: "addr:city",
 | 
			
		||||
    Website: "website",
 | 
			
		||||
    Email: "email",
 | 
			
		||||
    "Aantal aangepaste parkeerplaatsen": "capacity:disabled",
 | 
			
		||||
    "Aantal treden": "step_count",
 | 
			
		||||
    "Hellend vlak aanwezig": "ramp",
 | 
			
		||||
    "Baby verzorging aanwezig": "changing_table",
 | 
			
		||||
    "Totale hoogte van de treden": "kerb:height",
 | 
			
		||||
    "Deurbreedte": "door:width",
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * In some cases types might need to be converted as well
 | 
			
		||||
   *
 | 
			
		||||
   * Keys are the OSM properties, values are the wanted type
 | 
			
		||||
   */
 | 
			
		||||
  public static types = {
 | 
			
		||||
    "Hellend vlak aanwezig": "boolean",
 | 
			
		||||
    "Baby verzorging aanwezig": "boolean",
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Some tags also need to have units added
 | 
			
		||||
   */
 | 
			
		||||
  public static units = {
 | 
			
		||||
    "Totale hoogte van de treden": "cm",
 | 
			
		||||
    "Deurbreedte": "cm",
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										234
									
								
								scripts/onwheels/convertData.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										234
									
								
								scripts/onwheels/convertData.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,234 @@
 | 
			
		|||
import { parse } from "csv-parse/sync";
 | 
			
		||||
import { readFileSync, writeFileSync } from "fs";
 | 
			
		||||
import { Feature, FeatureCollection, GeoJsonProperties } from "geojson";
 | 
			
		||||
import Constants from "./constants";
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Function to determine the tags for a category
 | 
			
		||||
 *
 | 
			
		||||
 * @param category The category of the item
 | 
			
		||||
 * @returns List of tags for the category
 | 
			
		||||
 */
 | 
			
		||||
function categoryTags(category: string): GeoJsonProperties {
 | 
			
		||||
  const tags = {
 | 
			
		||||
    tags: Object.keys(Constants.categories[category]).map((tag) => {
 | 
			
		||||
      return `${tag}=${Constants.categories[category][tag]}`;
 | 
			
		||||
    }),
 | 
			
		||||
  };
 | 
			
		||||
  if (!tags) {
 | 
			
		||||
    throw `Unknown category: ${category}`;
 | 
			
		||||
  }
 | 
			
		||||
  return tags;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Rename tags to match the OSM standard
 | 
			
		||||
 *
 | 
			
		||||
 * @param item The item to convert
 | 
			
		||||
 * @returns GeoJsonProperties for the item
 | 
			
		||||
 */
 | 
			
		||||
function renameTags(item): GeoJsonProperties {
 | 
			
		||||
  const properties: GeoJsonProperties = {};
 | 
			
		||||
  properties.tags = [];
 | 
			
		||||
  // Loop through the original item tags
 | 
			
		||||
  for (const key in item) {
 | 
			
		||||
    // Check if we need it and it's not a null value
 | 
			
		||||
    if (Constants.names[key] && item[key]) {
 | 
			
		||||
      // Name and id tags need to be in the properties
 | 
			
		||||
      if (Constants.names[key] == "name" || Constants.names[key] == "id") {
 | 
			
		||||
        properties[Constants.names[key]] = item[key];
 | 
			
		||||
      }
 | 
			
		||||
      // Other tags need to be in the tags variable
 | 
			
		||||
      if (Constants.names[key] !== "id") {
 | 
			
		||||
        // Website needs to have at least any = encoded
 | 
			
		||||
        if(Constants.names[key] == "website") {
 | 
			
		||||
          let website = item[key];
 | 
			
		||||
          // Encode URL
 | 
			
		||||
          website = website.replace("=", "%3D");
 | 
			
		||||
          item[key] = website;
 | 
			
		||||
        }
 | 
			
		||||
        properties.tags.push(Constants.names[key] + "=" + item[key]);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return properties;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Convert types to match the OSM standard
 | 
			
		||||
 * 
 | 
			
		||||
 * @param properties The properties to convert
 | 
			
		||||
 * @returns The converted properties
 | 
			
		||||
 */
 | 
			
		||||
function convertTypes(properties: GeoJsonProperties): GeoJsonProperties {
 | 
			
		||||
  // Split the tags into a list
 | 
			
		||||
  let tags = properties.tags.split(";");
 | 
			
		||||
 | 
			
		||||
  for (const tag in tags) {
 | 
			
		||||
    // Split the tag into key and value
 | 
			
		||||
    const key = tags[tag].split("=")[0];
 | 
			
		||||
    const value = tags[tag].split("=")[1];
 | 
			
		||||
    const originalKey = Object.keys(Constants.names).find(
 | 
			
		||||
      (tag) => Constants.names[tag] === key
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    if (Constants.types[originalKey]) {
 | 
			
		||||
      // We need to convert the value to the correct type
 | 
			
		||||
      let newValue;
 | 
			
		||||
      switch (Constants.types[originalKey]) {
 | 
			
		||||
        case "boolean":
 | 
			
		||||
          newValue = value === "1" ? "yes" : "no";
 | 
			
		||||
          break;
 | 
			
		||||
        default:
 | 
			
		||||
          newValue = value;
 | 
			
		||||
          break;
 | 
			
		||||
      }
 | 
			
		||||
      tags[tag] = `${key}=${newValue}`;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Rejoin the tags
 | 
			
		||||
  properties.tags = tags.join(";");
 | 
			
		||||
 | 
			
		||||
  // Return the properties
 | 
			
		||||
  return properties;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Function to add units to the properties if necessary
 | 
			
		||||
 *
 | 
			
		||||
 * @param properties The properties to add units to
 | 
			
		||||
 * @returns The properties with units added
 | 
			
		||||
 */
 | 
			
		||||
function addUnits(properties: GeoJsonProperties): GeoJsonProperties {
 | 
			
		||||
  // Split the tags into a list
 | 
			
		||||
  let tags = properties.tags.split(";");
 | 
			
		||||
 | 
			
		||||
  for (const tag in tags) {
 | 
			
		||||
    const key = tags[tag].split("=")[0];
 | 
			
		||||
    const value = tags[tag].split("=")[1];
 | 
			
		||||
    const originalKey = Object.keys(Constants.names).find(
 | 
			
		||||
      (tag) => Constants.names[tag] === key
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    // Check if the property needs units, and doesn't already have them
 | 
			
		||||
    if (Constants.units[originalKey] && value.match(/.*([A-z]).*/gi) === null) {
 | 
			
		||||
      tags[tag] = `${key}=${value} ${Constants.units[originalKey]}`;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Rejoin the tags
 | 
			
		||||
  properties.tags = tags.join(";");
 | 
			
		||||
 | 
			
		||||
  // Return the properties
 | 
			
		||||
  return properties;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Function that adds Maproulette instructions and blurb to each item
 | 
			
		||||
 *
 | 
			
		||||
 * @param properties The properties to add Maproulette tags to
 | 
			
		||||
 * @param item The original CSV item
 | 
			
		||||
 */
 | 
			
		||||
function addMaprouletteTags(properties: GeoJsonProperties, item: any): GeoJsonProperties {
 | 
			
		||||
  properties[
 | 
			
		||||
    "blurb"
 | 
			
		||||
  ] = `This is feature out of the ${item["Categorie"]} category.
 | 
			
		||||
  It may match another OSM item, if so, you can add any missing tags to it.
 | 
			
		||||
  If it doesn't match any other OSM item, you can create a new one.
 | 
			
		||||
  Here is a list of tags that can be added:
 | 
			
		||||
  ${properties["tags"].split(";").join("\n")}
 | 
			
		||||
  You can also easily import this item using MapComplete: https://mapcomplete.osm.be/onwheels.html#${properties["id"]}`;
 | 
			
		||||
  return properties;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Main function to convert original CSV into GeoJSON
 | 
			
		||||
 *
 | 
			
		||||
 * @param args List of arguments [input.csv]
 | 
			
		||||
 */
 | 
			
		||||
function main(args: string[]): void {
 | 
			
		||||
  const csvOptions = {
 | 
			
		||||
    columns: true,
 | 
			
		||||
    skip_empty_lines: true,
 | 
			
		||||
    trim: true,
 | 
			
		||||
  };
 | 
			
		||||
  const file = args[0];
 | 
			
		||||
  const output = args[1];
 | 
			
		||||
 | 
			
		||||
  // Create an empty list to store the converted features
 | 
			
		||||
  var items: Feature[] = [];
 | 
			
		||||
 | 
			
		||||
  // Read CSV file
 | 
			
		||||
  const csv: Record<any, string>[] = parse(readFileSync(file), csvOptions);
 | 
			
		||||
 | 
			
		||||
  // Loop through all the entries
 | 
			
		||||
  for (var i = 0; i < csv.length; i++) {
 | 
			
		||||
    const item = csv[i];
 | 
			
		||||
 | 
			
		||||
    // Determine coordinates
 | 
			
		||||
    const lat = Number(item["Latitude"]);
 | 
			
		||||
    const lon = Number(item["Longitude"]);
 | 
			
		||||
 | 
			
		||||
    // Check if coordinates are valid
 | 
			
		||||
    if (isNaN(lat) || isNaN(lon)) {
 | 
			
		||||
      throw `Not a valid lat or lon for entry ${i}: ${JSON.stringify(item)}`;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Create a new collection to store the converted properties
 | 
			
		||||
    var properties: GeoJsonProperties = {};
 | 
			
		||||
 | 
			
		||||
    // Add standard tags for category
 | 
			
		||||
    const category = item["Categorie"];
 | 
			
		||||
    const tagsCategory = categoryTags(category);
 | 
			
		||||
 | 
			
		||||
    // Add the rest of the needed tags
 | 
			
		||||
    properties = { ...properties, ...renameTags(item) };
 | 
			
		||||
 | 
			
		||||
    // Merge them together
 | 
			
		||||
    properties.tags = [...tagsCategory.tags, ...properties.tags];
 | 
			
		||||
    properties.tags = properties.tags.join(";");
 | 
			
		||||
 | 
			
		||||
    // Convert types
 | 
			
		||||
    properties = convertTypes(properties);
 | 
			
		||||
 | 
			
		||||
    // Add units if necessary
 | 
			
		||||
    properties = addUnits(properties);
 | 
			
		||||
 | 
			
		||||
    // Add Maproulette tags
 | 
			
		||||
    properties = addMaprouletteTags(properties, item);
 | 
			
		||||
 | 
			
		||||
    // Create the new feature
 | 
			
		||||
    const feature: Feature = {
 | 
			
		||||
      type: "Feature",
 | 
			
		||||
      id: item["ID"],
 | 
			
		||||
      geometry: {
 | 
			
		||||
        type: "Point",
 | 
			
		||||
        coordinates: [lon, lat],
 | 
			
		||||
      },
 | 
			
		||||
      properties,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // Push it to the list we created earlier
 | 
			
		||||
    items.push(feature);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Make a FeatureCollection out of it
 | 
			
		||||
  const featureCollection: FeatureCollection = {
 | 
			
		||||
    type: "FeatureCollection",
 | 
			
		||||
    features: items,
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  // Write the data to a file or output to the console
 | 
			
		||||
  if (output) {
 | 
			
		||||
    writeFileSync(
 | 
			
		||||
      `${output}.geojson`,
 | 
			
		||||
      JSON.stringify(featureCollection, null, 2)
 | 
			
		||||
    );
 | 
			
		||||
  } else {
 | 
			
		||||
    console.log(JSON.stringify(featureCollection));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Execute the main function, with the stripped arguments
 | 
			
		||||
main(process.argv.slice(2));
 | 
			
		||||
| 
						 | 
				
			
			@ -123,7 +123,7 @@ describe('RewriteSpecial', function () {
 | 
			
		|||
        const r = new RewriteSpecial().convert(tr, "test").result
 | 
			
		||||
        expect(r).to.deep.eq({
 | 
			
		||||
            "id": "uk_addresses_import_button",
 | 
			
		||||
            "render":  {'*': "{import_button(address,urpn_count=$urpn_count;ref:GB:uprn=$ref:GB:uprn$,Add this address,./assets/themes/uk_addresses/housenumber_add.svg,,,,none)}"}
 | 
			
		||||
            "render":  {'*': "{import_button(address,urpn_count=$urpn_count;ref:GB:uprn=$ref:GB:uprn$,Add this address,./assets/themes/uk_addresses/housenumber_add.svg,,,,none,)}"}
 | 
			
		||||
        })
 | 
			
		||||
    })
 | 
			
		||||
});
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue