| 
									
										
										
										
											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-07-18 16:10:06 +02:00
										 |  |  |         properties.tags.push(Constants.names[key] + "=" + item[key]); | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											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(";"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   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-07-14 15:17:09 +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-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(";"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   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-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-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]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // 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"]; | 
					
						
							| 
									
										
										
										
											2022-07-18 16:10:06 +02:00
										 |  |  |     const tagsCategory = categoryTags(category); | 
					
						
							| 
									
										
										
										
											2022-07-14 15:17:09 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // Add the rest of the needed tags
 | 
					
						
							|  |  |  |     properties = { ...properties, ...renameTags(item) }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-18 16:10:06 +02:00
										 |  |  |     // Merge them together
 | 
					
						
							|  |  |  |     properties.tags = [...tagsCategory.tags, ...properties.tags]; | 
					
						
							|  |  |  |     properties.tags = properties.tags.join(";"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-14 15:17:09 +02:00
										 |  |  |     // Convert types
 | 
					
						
							|  |  |  |     properties = convertTypes(properties); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Add units if necessary
 | 
					
						
							| 
									
										
										
										
											2022-07-19 12:09:04 +02:00
										 |  |  |     properties = addUnits(properties); | 
					
						
							| 
									
										
										
										
											2022-07-14 15:17:09 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-18 16:10:06 +02:00
										 |  |  |     // Add Maproulette tags
 | 
					
						
							|  |  |  |     properties = addMaprouletteTags(properties, item); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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, | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Push it to the list we created earlier
 | 
					
						
							|  |  |  |     items.push(feature); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Make a FeatureCollection out of it
 | 
					
						
							|  |  |  |   const featureCollection: FeatureCollection = { | 
					
						
							|  |  |  |     type: "FeatureCollection", | 
					
						
							|  |  |  |     features: items, | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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)); |