| 
									
										
										
										
											2022-07-14 15:17:09 +02:00
										 |  |  | 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 { | 
					
						
							| 
									
										
										
										
											2022-07-18 16:10:06 +02:00
										 |  |  |     const tags = { | 
					
						
							|  |  |  |         tags: Object.keys(Constants.categories[category]).map((tag) => { | 
					
						
							|  |  |  |             return `${tag}=${Constants.categories[category][tag]}` | 
					
						
							|  |  |  |         }), | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-07-14 15:17:09 +02:00
										 |  |  |     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 = {} | 
					
						
							| 
									
										
										
										
											2022-07-18 16:10:06 +02:00
										 |  |  |     properties.tags = [] | 
					
						
							| 
									
										
										
										
											2022-07-19 20:54:05 +02:00
										 |  |  |     // Loop through the original item tags
 | 
					
						
							| 
									
										
										
										
											2022-07-14 15:17:09 +02:00
										 |  |  |     for (const key in item) { | 
					
						
							| 
									
										
										
										
											2022-07-19 20:54:05 +02:00
										 |  |  |         // Check if we need it and it's not a null value
 | 
					
						
							| 
									
										
										
										
											2022-07-14 15:17:09 +02:00
										 |  |  |         if (Constants.names[key] && item[key]) { | 
					
						
							| 
									
										
										
										
											2022-07-19 20:54:05 +02:00
										 |  |  |             // Name and id tags need to be in the properties
 | 
					
						
							| 
									
										
										
										
											2022-07-18 16:10:06 +02:00
										 |  |  |             if (Constants.names[key] == "name" || Constants.names[key] == "id") { | 
					
						
							|  |  |  |                 properties[Constants.names[key]] = item[key] | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2022-07-19 20:54:05 +02:00
										 |  |  |             // Other tags need to be in the tags variable
 | 
					
						
							| 
									
										
										
										
											2022-07-18 16:10:06 +02:00
										 |  |  |             if (Constants.names[key] !== "id") { | 
					
						
							| 
									
										
										
										
											2022-07-19 20:54:05 +02:00
										 |  |  |                 // 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 | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |                 } | 
					
						
							| 
									
										
										
										
											2022-07-18 16:10:06 +02:00
										 |  |  |                 properties.tags.push(Constants.names[key] + "=" + item[key]) | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2022-07-19 20:54:05 +02:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2022-07-14 15:17:09 +02:00
										 |  |  |     } | 
					
						
							|  |  |  |     return properties | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-19 20:54:05 +02:00
										 |  |  | /** | 
					
						
							|  |  |  |  * Convert types to match the OSM standard | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * @param properties The properties to convert | 
					
						
							|  |  |  |  * @returns The converted properties | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2022-07-14 15:17:09 +02:00
										 |  |  | function convertTypes(properties: GeoJsonProperties): GeoJsonProperties { | 
					
						
							| 
									
										
										
										
											2022-07-19 12:09:04 +02:00
										 |  |  |     // Split the tags into a list
 | 
					
						
							|  |  |  |     let tags = properties.tags.split(";") | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-19 12:09:04 +02:00
										 |  |  |     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) | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-19 12:09:04 +02:00
										 |  |  |         if (Constants.types[originalKey]) { | 
					
						
							|  |  |  |             // We need to convert the value to the correct type
 | 
					
						
							|  |  |  |             let newValue | 
					
						
							|  |  |  |             switch (Constants.types[originalKey]) { | 
					
						
							| 
									
										
										
										
											2022-07-14 15:17:09 +02:00
										 |  |  |                 case "boolean": | 
					
						
							| 
									
										
										
										
											2022-07-19 12:09:04 +02:00
										 |  |  |                     newValue = value === "1" ? "yes" : "no" | 
					
						
							| 
									
										
										
										
											2022-07-14 15:17:09 +02:00
										 |  |  |                     break | 
					
						
							|  |  |  |                 default: | 
					
						
							| 
									
										
										
										
											2022-07-19 12:09:04 +02:00
										 |  |  |                     newValue = value | 
					
						
							| 
									
										
										
										
											2022-07-14 15:17:09 +02:00
										 |  |  |                     break | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2022-07-19 12:09:04 +02:00
										 |  |  |             tags[tag] = `${key}=${newValue}` | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2022-07-14 15:17:09 +02:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-07-19 12:09:04 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // Rejoin the tags
 | 
					
						
							|  |  |  |     properties.tags = tags.join(";") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Return the properties
 | 
					
						
							| 
									
										
										
										
											2022-07-14 15:17:09 +02:00
										 |  |  |     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 { | 
					
						
							| 
									
										
										
										
											2022-07-19 12:09:04 +02:00
										 |  |  |     // Split the tags into a list
 | 
					
						
							|  |  |  |     let tags = properties.tags.split(";") | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-19 12:09:04 +02:00
										 |  |  |     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) | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-14 15:17:09 +02:00
										 |  |  |         // Check if the property needs units, and doesn't already have them
 | 
					
						
							| 
									
										
										
										
											2022-07-19 12:09:04 +02:00
										 |  |  |         if (Constants.units[originalKey] && value.match(/.*([A-z]).*/gi) === null) { | 
					
						
							|  |  |  |             tags[tag] = `${key}=${value} ${Constants.units[originalKey]}` | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2022-07-14 15:17:09 +02:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-07-19 12:09:04 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // Rejoin the tags
 | 
					
						
							|  |  |  |     properties.tags = tags.join(";") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Return the properties
 | 
					
						
							| 
									
										
										
										
											2022-07-14 15:17:09 +02:00
										 |  |  |     return properties | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-18 16:10:06 +02:00
										 |  |  | /** | 
					
						
							|  |  |  |  * 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 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-14 15:17:09 +02:00
										 |  |  | /** | 
					
						
							|  |  |  |  * 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] | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-14 15:17:09 +02:00
										 |  |  |     // Create an empty list to store the converted features
 | 
					
						
							|  |  |  |     var items: Feature[] = [] | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-14 15:17:09 +02:00
										 |  |  |     // Read CSV file
 | 
					
						
							|  |  |  |     const csv: Record<any, string>[] = parse(readFileSync(file), csvOptions) | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-14 15:17:09 +02:00
										 |  |  |     // Loop through all the entries
 | 
					
						
							|  |  |  |     for (var i = 0; i < csv.length; i++) { | 
					
						
							|  |  |  |         const item = csv[i] | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-14 15:17:09 +02:00
										 |  |  |         // Determine coordinates
 | 
					
						
							|  |  |  |         const lat = Number(item["Latitude"]) | 
					
						
							|  |  |  |         const lon = Number(item["Longitude"]) | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-14 15:17:09 +02:00
										 |  |  |         // Check if coordinates are valid
 | 
					
						
							|  |  |  |         if (isNaN(lat) || isNaN(lon)) { | 
					
						
							|  |  |  |             throw `Not a valid lat or lon for entry ${i}: ${JSON.stringify(item)}` | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-14 15:17:09 +02:00
										 |  |  |         // Create a new collection to store the converted properties
 | 
					
						
							|  |  |  |         var properties: GeoJsonProperties = {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Add standard tags for category
 | 
					
						
							|  |  |  |         const category = item["Categorie"] | 
					
						
							| 
									
										
										
										
											2022-07-18 16:10:06 +02:00
										 |  |  |         const tagsCategory = categoryTags(category) | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-14 15:17:09 +02:00
										 |  |  |         // Add the rest of the needed tags
 | 
					
						
							|  |  |  |         properties = { ...properties, ...renameTags(item) } | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-18 16:10:06 +02:00
										 |  |  |         // Merge them together
 | 
					
						
							|  |  |  |         properties.tags = [...tagsCategory.tags, ...properties.tags] | 
					
						
							|  |  |  |         properties.tags = properties.tags.join(";") | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-14 15:17:09 +02:00
										 |  |  |         // Convert types
 | 
					
						
							|  |  |  |         properties = convertTypes(properties) | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-14 15:17:09 +02:00
										 |  |  |         // Add units if necessary
 | 
					
						
							| 
									
										
										
										
											2022-07-19 12:09:04 +02:00
										 |  |  |         properties = addUnits(properties) | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-18 16:10:06 +02:00
										 |  |  |         // Add Maproulette tags
 | 
					
						
							|  |  |  |         properties = addMaprouletteTags(properties, item) | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-14 15:17:09 +02:00
										 |  |  |         // Create the new feature
 | 
					
						
							|  |  |  |         const feature: Feature = { | 
					
						
							|  |  |  |             type: "Feature", | 
					
						
							|  |  |  |             id: item["ID"], | 
					
						
							|  |  |  |             geometry: { | 
					
						
							|  |  |  |                 type: "Point", | 
					
						
							|  |  |  |                 coordinates: [lon, lat], | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |             properties, | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-14 15:17:09 +02:00
										 |  |  |         // Push it to the list we created earlier
 | 
					
						
							|  |  |  |         items.push(feature) | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-14 15:17:09 +02:00
										 |  |  |     // Make a FeatureCollection out of it
 | 
					
						
							|  |  |  |     const featureCollection: FeatureCollection = { | 
					
						
							|  |  |  |         type: "FeatureCollection", | 
					
						
							|  |  |  |         features: items, | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-19 12:09:04 +02:00
										 |  |  |     // Write the data to a file or output to the console
 | 
					
						
							| 
									
										
										
										
											2022-07-14 15:17:09 +02:00
										 |  |  |     if (output) { | 
					
						
							| 
									
										
										
										
											2022-07-18 16:10:06 +02:00
										 |  |  |         writeFileSync(`${output}.geojson`, JSON.stringify(featureCollection, null, 2)) | 
					
						
							| 
									
										
										
										
											2022-07-19 12:09:04 +02:00
										 |  |  |     } else { | 
					
						
							|  |  |  |         console.log(JSON.stringify(featureCollection)) | 
					
						
							| 
									
										
										
										
											2022-07-14 15:17:09 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Execute the main function, with the stripped arguments
 | 
					
						
							|  |  |  | main(process.argv.slice(2)) |