diff --git a/maps b/maps new file mode 120000 index 0000000..7f0f314 --- /dev/null +++ b/maps @@ -0,0 +1 @@ +maps/ \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index b1a47e4..76d777d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,10 +10,12 @@ "license": "MIT", "dependencies": { "@mapbox/mapbox-gl-rtl-text": "^0.3.0", - "maplibre-gl": "5.8.0" + "maplibre-gl": "5.8.0", + "open-location-code": "^1.0.3" }, "devDependencies": { "@types/node": "24.6.1", + "@types/open-location-code": "^1.0.1", "@typescript-eslint/eslint-plugin": "5.29.0", "@typescript-eslint/parser": "5.29.0", "builtin-modules": "3.3.0", @@ -793,6 +795,12 @@ "undici-types": "~7.13.0" } }, + "node_modules/@types/open-location-code": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/open-location-code/-/open-location-code-1.0.1.tgz", + "integrity": "sha512-txJKhrRpT2fw8RgZQUeT6qycBVIzRQ2zD7ecdLvc5R2QaeqSLcYIh1jxLSC+LjcpUT26Qyfw2UTttmhgk3sWmg==", + "dev": true + }, "node_modules/@types/supercluster": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/@types/supercluster/-/supercluster-7.1.3.tgz", @@ -2196,6 +2204,11 @@ "wrappy": "1" } }, + "node_modules/open-location-code": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/open-location-code/-/open-location-code-1.0.3.tgz", + "integrity": "sha512-DBm14BSn40Ee241n80zIFXIT6+y8Tb0I+jTdosLJ8Sidvr2qONvymwqymVbHV2nS+1gkDZ5eTNpnOIVV0Kn2fw==" + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", diff --git a/package.json b/package.json index 8ef8e15..70200f0 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "license": "MIT", "devDependencies": { "@types/node": "24.6.1", + "@types/open-location-code": "^1.0.1", "@typescript-eslint/eslint-plugin": "5.29.0", "@typescript-eslint/parser": "5.29.0", "builtin-modules": "3.3.0", @@ -29,6 +30,7 @@ }, "dependencies": { "@mapbox/mapbox-gl-rtl-text": "^0.3.0", - "maplibre-gl": "5.8.0" + "maplibre-gl": "5.8.0", + "open-location-code": "^1.0.3" } } diff --git a/src/map/utils.ts b/src/map/utils.ts index 9e70d0c..1d0e562 100644 --- a/src/map/utils.ts +++ b/src/map/utils.ts @@ -1,4 +1,7 @@ import { Value, NumberValue, StringValue, ListValue } from 'obsidian'; +import { OpenLocationCode } from 'open-location-code'; + +const olc = new OpenLocationCode(); /** * Converts a Value to coordinate tuple [lat, lng] @@ -14,13 +17,25 @@ export function coordinateFromValue(value: Value | null): [number, number] | nul lng = parseCoordinate(value.get(1)); } } - // Handle string values (e.g., "34.1395597,-118.3870991" or "34.1395597, -118.3870991") + // Handle string values + // may be string coordinates (e.g., "34.1395597,-118.3870991" or "34.1395597, -118.3870991") + // or Plus Code (e.g. "9C3XGVHC+XW") else if (value instanceof StringValue) { - // Split by comma and handle various spacing - const parts = value.toString().trim().split(','); - if (parts.length >= 2) { - lat = parseCoordinate(parts[0].trim()); - lng = parseCoordinate(parts[1].trim()); + const str = value.toString().trim(); + + // Check for full Plus Code (e.g., "9C3XGVHC+XW") + if (olc.isFull(str)) { + const area = olc.decode(str); + lat = area.latitudeCenter; + lng = area.longitudeCenter; + } + // Fall back to comma-separated coordinates (e.g., "34.1395597, -118.3870991") + else { + const parts = str.split(','); + if (parts.length >= 2) { + lat = parseCoordinate(parts[0].trim()); + lng = parseCoordinate(parts[1].trim()); + } } } diff --git a/tests/generate_test_files.py b/tests/generate_test_files.py index c27dc9d..3774b1f 100644 --- a/tests/generate_test_files.py +++ b/tests/generate_test_files.py @@ -3,10 +3,11 @@ Generate test markdown files with valid coordinates for Obsidian Maps plugin testing. Usage: - python generate_test_files.py [count] + python generate_test_files.py [count] [pluscode_count] Arguments: - count: Number of files to generate (default: 100) + count: Number of coordinate files to generate (default: 100) + pluscode_count: Number of Plus Code files to generate (default: 20) """ import os @@ -145,18 +146,56 @@ def create_markdown_file(directory, filename, coordinates, place_type): f.write(content) -def generate_test_files(count=100, output_dir="generated_places"): - """Generate test markdown files with coordinates.""" +PLUS_CODE_PLACES = [ + {"name": "British Library", "plus_code": "9C3XGVJC+2W", "type": "[[Library]]"}, + {"name": "Eiffel Tower", "plus_code": "8FW4V75V+9R", "type": "[[Monument]]"}, + {"name": "Statue of Liberty", "plus_code": "87G7MXQ4+M6", "type": "[[Monument]]"}, + {"name": "Taj Mahal", "plus_code": "7JVW52GR+2R", "type": "[[Monument]]"}, + {"name": "Colosseum", "plus_code": "8FHJVFRR+3V", "type": "[[Monument]]"}, + {"name": "Great Wall of China", "plus_code": "8PGRCHJC+Q5", "type": "[[Monument]]"}, + {"name": "Great Pyramid of Giza", "plus_code": "7GXHX4HM+MM", "type": "[[Monument]]"}, + {"name": "Sydney Opera House", "plus_code": "4RRH46V8+74", "type": "[[Theater]]"}, + {"name": "Machu Picchu", "plus_code": "57R9RFP4+Q2", "type": "[[Monument]]"}, + {"name": "Burj Khalifa", "plus_code": "7HQQ57WF+VQ", "type": "[[Building]]"}, + {"name": "Christ the Redeemer", "plus_code": "589R2QXQ+6R", "type": "[[Monument]]"}, + {"name": "Big Ben", "plus_code": "9C3XGV2G+75", "type": "[[Monument]]"}, + {"name": "Golden Gate Bridge", "plus_code": "849VRG9C+XM", "type": "[[Monument]]"}, + {"name": "Acropolis of Athens", "plus_code": "8G95XPCG+J7", "type": "[[Monument]]"}, + {"name": "Petra", "plus_code": "8G2Q8CHV+CQ", "type": "[[Monument]]"}, + {"name": "Angkor Wat", "plus_code": "7P55CV78+2R", "type": "[[Temple]]"}, + {"name": "Sagrada Familia", "plus_code": "8FH4C53F+CQ", "type": "[[Cathedral]]"}, + {"name": "Mount Everest", "plus_code": "7MV8XWQG+62", "type": "[[Mountain]]"}, + {"name": "Hollywood Sign", "plus_code": "85634MMH+JC", "type": "[[Monument]]"}, + {"name": "Forbidden City", "plus_code": "8PFRW98W+GV", "type": "[[Palace]]"}, + {"name": "Stonehenge", "plus_code": "9C3W55HF+HG", "type": "[[Monument]]"}, +] + + +def create_plus_code_file(directory, place): + """Create a markdown file using a Plus Code for location.""" + content = f"""--- +category: "[[Places]]" +type: "{place['type']}" +location: "{place['plus_code']}" +--- +""" + filepath = directory / f"{place['name']}.md" + with open(filepath, 'w', encoding='utf-8') as f: + f.write(content) + + +def generate_test_files(count=100, pluscode_count=20, output_dir="generated_places"): + """Generate test markdown files with coordinates and Plus Code locations.""" # Create output directory script_dir = Path(__file__).parent output_path = script_dir / output_dir output_path.mkdir(exist_ok=True) - - print(f"Generating {count} test files in {output_path}...") - + + print(f"Generating {count} coordinate files in {output_path}...") + # Keep track of generated names to avoid duplicates generated_names = set() - + for i in range(count): # Generate unique place name attempt = 0 @@ -169,29 +208,40 @@ def generate_test_files(count=100, output_dir="generated_places"): else: # If we can't find a unique name, append a number place_name = f"{generate_random_place_name()} {i}" - + # Generate coordinates and type coordinates = generate_coordinates() place_type = generate_place_type() - + # Create filename filename = f"{place_name}.md" - + # Create the file create_markdown_file(output_path, filename, coordinates, place_type) - + # Print progress for large batches if (i + 1) % 1000 == 0: print(f" Generated {i + 1} files...") - - print(f"✓ Successfully generated {count} files in {output_path}/") + + print(f"✓ Successfully generated {count} coordinate files in {output_path}/") + + # Generate Plus Code files + places = PLUS_CODE_PLACES[:pluscode_count] + print(f"Generating {len(places)} Plus Code file(s) in {output_path}...") + + for place in places: + create_plus_code_file(output_path, place) + + print(f"✓ Successfully generated {len(places)} Plus Code file(s) in {output_path}/") + return output_path def main(): """Main entry point.""" count = 100 # Default - + pluscode_count = 20 # Default + if len(sys.argv) > 1: try: count = int(sys.argv[1]) @@ -201,8 +251,18 @@ def main(): except ValueError: print(f"Error: Invalid count '{sys.argv[1]}'. Must be an integer.") sys.exit(1) - - generate_test_files(count) + + if len(sys.argv) > 2: + try: + pluscode_count = int(sys.argv[2]) + if pluscode_count < 0: + print("Error: Plus code count must be a non-negative integer") + sys.exit(1) + except ValueError: + print(f"Error: Invalid pluscode_count '{sys.argv[2]}'. Must be an integer.") + sys.exit(1) + + generate_test_files(count, pluscode_count) if __name__ == "__main__":