├── .bazeliskrc ├── .bazelrc ├── .github └── workflows │ └── main.yml ├── .gitignore ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Documentation ├── External_Implementations.md ├── FAQ.md ├── README.md ├── Reference │ ├── App_Developers.md │ ├── Field_Collection_Data_Practices.md │ ├── GIS_Software.md │ ├── Plus_Code_Users.md │ ├── Using_Spreadsheets.md │ ├── comparison.adoc │ └── plus.codes_Website_API.md ├── Specification │ ├── Naming_Guidelines.md │ ├── olc_definition.adoc │ └── specification.md └── images │ ├── code_areas.png │ ├── geohash36_grid.png │ ├── olc_10_character.png │ ├── olc_11_grid.png │ └── openpostcode_grid.png ├── FAQ.txt ├── LICENSE ├── MODULE.bazel ├── README.md ├── TESTING.md ├── WORKSPACE ├── android_demo ├── .gitignore ├── README.md ├── android │ ├── build.gradle │ ├── libs │ │ └── README.md │ ├── proguard-rules.pro │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ └── com │ │ │ └── openlocationcode │ │ │ └── android │ │ │ ├── code │ │ │ ├── CodeContract.java │ │ │ ├── CodePresenter.java │ │ │ ├── CodeView.java │ │ │ └── OpenLocationCodeUtil.java │ │ │ ├── current │ │ │ ├── GoogleApiModule.java │ │ │ ├── LocationProvider.java │ │ │ ├── LocationProviderFactoryComponent.java │ │ │ └── LocationUtil.java │ │ │ ├── direction │ │ │ ├── Direction.java │ │ │ ├── DirectionContract.java │ │ │ ├── DirectionPresenter.java │ │ │ ├── DirectionUtil.java │ │ │ └── DirectionView.java │ │ │ ├── localities │ │ │ └── Locality.java │ │ │ ├── main │ │ │ ├── MainActivity.java │ │ │ ├── MainFragment.java │ │ │ ├── MainPresenter.java │ │ │ └── WelcomeActivity.java │ │ │ ├── map │ │ │ ├── MapContract.java │ │ │ ├── MapPresenter.java │ │ │ └── MyMapView.java │ │ │ └── search │ │ │ ├── AutoCompleteEditor.java │ │ │ ├── SearchContract.java │ │ │ ├── SearchPresenter.java │ │ │ └── SearchView.java │ │ └── res │ │ ├── drawable-hdpi │ │ ├── ic_action_maps_navigation.png │ │ ├── ic_launcher.png │ │ ├── map_center.png │ │ └── welcome_image.png │ │ ├── drawable-ldpi │ │ └── map_center.png │ │ ├── drawable-mdpi │ │ ├── ic_action_maps_navigation.png │ │ ├── ic_launcher.png │ │ └── map_center.png │ │ ├── drawable-xhdpi │ │ ├── ic_action_maps_navigation.png │ │ ├── ic_launcher.png │ │ ├── map_center.png │ │ └── welcome_image.png │ │ ├── drawable-xxhdpi │ │ ├── ic_action_maps_navigation.png │ │ ├── ic_launcher.png │ │ ├── map_center.png │ │ └── welcome_image.png │ │ ├── drawable-xxxhdpi │ │ ├── ic_action_maps_navigation.png │ │ ├── ic_launcher.png │ │ └── map_center.png │ │ ├── drawable │ │ ├── map_satellite_button.xml │ │ ├── rounded_layout.xml │ │ └── search_button.xml │ │ ├── layout │ │ ├── code.xml │ │ ├── direction.xml │ │ ├── main_act.xml │ │ ├── main_frag.xml │ │ ├── my_map.xml │ │ ├── search.xml │ │ └── welcome.xml │ │ ├── menu │ │ └── share_menu.xml │ │ ├── values-pt │ │ └── strings.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml ├── build.gradle ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── openlocationcode_android.iml └── settings.gradle ├── c ├── .clang-format ├── .gitignore ├── BUILD ├── README.md ├── benchmark │ ├── .gitignore │ ├── Makefile │ ├── bm-c.c │ ├── bm-cpp.cc │ └── shared.c ├── clang_check.sh ├── example.c ├── openlocationcode_test.cc └── src │ ├── olc.c │ ├── olc.h │ └── olc_private.h ├── cpp ├── .clang-format ├── .gitignore ├── BUILD ├── README.md ├── clang_check.sh ├── codearea.cc ├── codearea.h ├── openlocationcode.cc ├── openlocationcode.h ├── openlocationcode_example.cc └── openlocationcode_test.cc ├── dart ├── .gitignore ├── README.md ├── checks.sh ├── lib │ ├── open_location_code.dart │ └── src │ │ └── open_location_code.dart ├── pubspec.yaml └── test │ ├── benchmark_test.dart │ ├── clip_latitude_test.dart │ ├── compute_precision_test.dart │ ├── decode_test.dart │ ├── encode_test.dart │ ├── short_code_test.dart │ ├── utils.dart │ └── validity_test.dart ├── garmin └── PlusCodeDatafield │ ├── .project │ ├── README.md │ ├── manifest.xml │ ├── monkey.jungle │ ├── resources-d2bravo │ ├── drawables.xml │ └── launcher_icon.png │ ├── resources-d2bravo_titanium │ ├── drawables.xml │ └── launcher_icon.png │ ├── resources-edge1030 │ ├── drawables.xml │ └── launcher_icon.png │ ├── resources-edge1030bontrager │ ├── drawables.xml │ └── launcher_icon.png │ ├── resources-edge130 │ ├── drawables.xml │ └── launcher_icon.png │ ├── resources-edge520plus │ ├── drawables.xml │ └── launcher_icon.png │ ├── resources-edge820 │ ├── drawables.xml │ └── launcher_icon.png │ ├── resources-edge_1000 │ ├── drawables.xml │ └── launcher_icon.png │ ├── resources-edge_520 │ ├── drawables.xml │ └── launcher_icon.png │ ├── resources-epix │ ├── drawables.xml │ └── launcher_icon.png │ ├── resources-fenix3 │ ├── drawables.xml │ └── launcher_icon.png │ ├── resources-fenix3_hr │ ├── drawables.xml │ └── launcher_icon.png │ ├── resources-fenix5 │ ├── drawables.xml │ └── launcher_icon.png │ ├── resources-fenix5plus │ ├── drawables.xml │ └── launcher_icon.png │ ├── resources-fenix5s │ ├── drawables.xml │ └── launcher_icon.png │ ├── resources-fenix5x │ ├── drawables.xml │ └── launcher_icon.png │ ├── resources-fenixchronos │ ├── drawables.xml │ └── launcher_icon.png │ ├── resources-fr235 │ ├── drawables.xml │ └── launcher_icon.png │ ├── resources-fr630 │ ├── drawables.xml │ └── launcher_icon.png │ ├── resources-fr645 │ ├── drawables.xml │ └── launcher_icon.png │ ├── resources-fr645m │ ├── drawables.xml │ └── launcher_icon.png │ ├── resources-fr735xt │ ├── drawables.xml │ └── launcher_icon.png │ ├── resources-fr920xt │ ├── drawables.xml │ └── launcher_icon.png │ ├── resources-fr935 │ ├── drawables.xml │ └── launcher_icon.png │ ├── resources-oregon7xx │ ├── drawables.xml │ └── launcher_icon.png │ ├── resources-rino7xx │ ├── drawables.xml │ └── launcher_icon.png │ ├── resources-vivoactive │ ├── drawables.xml │ └── launcher_icon.png │ ├── resources-vivoactive3 │ ├── drawables.xml │ └── launcher_icon.png │ ├── resources-vivoactive3m │ ├── drawables.xml │ └── launcher_icon.png │ ├── resources-vivoactive_hr │ ├── drawables.xml │ ├── launcher_icon.png │ └── resources-fenix3_hr │ │ ├── drawables.xml │ │ └── launcher_icon.png │ ├── resources │ ├── drawables.xml │ ├── launcher_icon.png │ ├── layouts.xml │ └── strings.xml │ └── source │ ├── PlusCodeBackground.mc │ ├── PlusCodeDatafield.mc │ └── PlusCodeView.mc ├── go ├── .gitignore ├── README.md ├── corpus │ ├── .gitignore │ └── gen.go ├── decode.go ├── encode.go ├── go.mod ├── go.sum ├── olc.go ├── olc_gofuzz.go ├── olc_test.go └── shorten.go ├── java ├── .gitignore ├── BUILD ├── README.md ├── pom.xml └── src │ ├── main │ └── java │ │ └── com │ │ └── google │ │ └── openlocationcode │ │ └── OpenLocationCode.java │ └── test │ └── java │ └── com │ └── google │ └── openlocationcode │ ├── BenchmarkTest.java │ ├── DecodingTest.java │ ├── EncodingTest.java │ ├── PrecisionTest.java │ ├── RecoverTest.java │ ├── ShorteningTest.java │ ├── TestUtils.java │ ├── UtilsTest.java │ └── ValidityTest.java ├── js ├── .eslintrc.js ├── README.md ├── checks.sh ├── closure │ ├── BUILD │ ├── README.md │ ├── openlocationcode.js │ └── openlocationcode_test.js ├── contrib │ ├── README.md │ ├── olc_grid_overlay.js │ └── olc_grid_overlay.min.js ├── examples │ ├── example1.html │ ├── example2.html │ ├── example3.html │ ├── examples.css │ └── examples.js ├── gulpfile.js ├── package.json ├── src │ ├── openlocationcode.js │ └── openlocationcode.min.js └── test │ ├── jasmine-tests.js │ └── karma.config.js ├── package.json ├── plpgsql ├── README.md ├── pluscode_functions.sql └── tests_script_l.sql ├── python ├── .gitignore ├── .style.yapf ├── BUILD ├── README.md ├── format_check.sh ├── openlocationcode │ ├── __init__.py │ └── openlocationcode.py ├── openlocationcode_test.py └── setup.py ├── ruby ├── README.md ├── lib │ ├── plus_codes.rb │ └── plus_codes │ │ ├── code_area.rb │ │ └── open_location_code.rb ├── open-location-code.gemspec ├── rubocop.yml └── test │ └── plus_codes_test.rb ├── rust ├── Cargo.toml ├── README.md ├── src │ ├── codearea.rs │ ├── consts.rs │ ├── interface.rs │ ├── lib.rs │ └── private.rs └── tests │ ├── all_test.rs │ └── csv_reader.rs ├── test_data ├── BUILD ├── csv_to_json.go ├── decoding.csv ├── encoding.csv ├── shortCodeTests.csv └── validityTests.csv ├── tile_server ├── README.md ├── example.html ├── gridserver │ ├── geojson.go │ ├── geojson_test.go │ ├── go.mod │ ├── go.sum │ ├── gridserver.go │ ├── image.go │ ├── image_test.go │ ├── mvt.go │ ├── mvt_test.go │ ├── projection.go │ ├── projection_test.go │ ├── testdata │ │ ├── 21_1098232_1362659.json │ │ ├── 21_1098232_1362659.mvt │ │ ├── 21_1098232_1362659.png │ │ ├── 21_1098232_1362659_geodetic.json │ │ ├── 21_1098232_1362659_geodetic.mvt │ │ ├── 21_1098232_1362659_geodetic.png │ │ ├── 5_17_19.json │ │ ├── 5_17_19.mvt │ │ ├── 5_17_19.png │ │ ├── 5_17_19_geodetic.json │ │ ├── 5_17_19_geodetic.mvt │ │ ├── 5_17_19_white_zoom_2.png │ │ ├── 5_17_19_zoom_2.json │ │ └── 5_17_19_zoom_2.mvt │ └── tileref.go └── main.go └── visualbasic ├── OpenLocationCode.bas └── README.md /.bazeliskrc: -------------------------------------------------------------------------------- 1 | # Set options for Bazelisk, a wrapper for Bazel. 2 | 3 | # Set the version of Bazel to use. 4 | # TODO: #642 -- Update once the JS bazelbuild/rules_closure supports bazel modules. 5 | USE_BAZEL_VERSION=7.4.1 6 | -------------------------------------------------------------------------------- /.bazelrc: -------------------------------------------------------------------------------- 1 | # Reduce OOM errors on TravisCI 2 | startup --host_jvm_args=-Xms500m 3 | startup --host_jvm_args=-Xmx500m 4 | startup --host_jvm_args=-XX:-UseParallelGC 5 | # build --local_resources=400,2,1.0 6 | build --worker_max_instances=auto 7 | 8 | # Configure tests - increase timeout, print everything and timeout warnings 9 | test --verbose_failures --test_output=all --test_verbose_timeout_warnings 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore backup and swap files. 2 | *~ 3 | .*.swp 4 | # Bazel module lock file. 5 | MODULE.bazel.lock 6 | # Ignore all bazel-* links. 7 | /bazel-* 8 | # Ignore outputs generated during Bazel bootstrapping. 9 | /output/ 10 | # Ignore compiled files 11 | **/target/ 12 | # Ignore intelliJ auto generated dir 13 | **/.idea/ 14 | # Ignore iml extensions files 15 | **/*.iml 16 | # Ignore visual studio auto generated dir 17 | **/.vscode/ 18 | # Ignore JS NPM modules 19 | /js/node_modules/ 20 | /js/package-lock.json 21 | # Ignore dynamically generated test JSON files. 22 | /js/test/*json 23 | # Ignore Rust lockfile. 24 | /rust/Cargo.lock 25 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | # 2015-01-30 Formatting Change 3 | 4 | Code formatting was modified to make the common cases easier to recognise and use. 5 | A typical house level code of 10 digits is now represented as XXXXXXXX+XX, making 6 | the common short cases XXXX+XX and XX+XX. 7 | -------------------------------------------------------------------------------- /Documentation/External_Implementations.md: -------------------------------------------------------------------------------- 1 | # External Implementations 2 | 3 | This page lists implementations of the Open Location Code library by third parties. 4 | These include implementations in languages other than the those in this repository. 5 | 6 | These implementations have not been tested or reviewed, and are included here as a convenience. 7 | 8 | | Language | Link | Notes | 9 | | ------------------ | ----------------------------------------------------------------------------------- | -------------------------------------------------------------------- | 10 | | Ada | https://github.com/malaise/ada/ | | 11 | | C# (.NET standard) | https://github.com/JonMcPherson/open-location-code | | 12 | | Common Lisp | https://github.com/ralph-schleicher/open-location-code | | 13 | | EMACS | https://gitlab.liu.se/davby02/olc | | 14 | | R | https://github.com/Ironholds/olctools | | 15 | | Solidity | https://etherscan.io/address/0xf85c6320cc60dec45af1f7ce82b13dd24d539690#code#F3#L28 | URL is a link to a contract, not a repo | 16 | | Swift 3.x, 4.x | https://github.com/curbmap/OpenLocationCode-swift | Not fully implemented | 17 | | Swift 5.0 | https://github.com/google/open-location-code-swift | This library supports Objective-C for iOS, macOS, tvOS, and watchOS. | 18 | | Typescript | https://github.com/tspoke/typescript-open-location-code | | 19 | -------------------------------------------------------------------------------- /Documentation/README.md: -------------------------------------------------------------------------------- 1 | # Documentation 2 | 3 | The wiki is where you can find out information about using the software, the codes, or the API. 4 | 5 | ### Common pages 6 | 7 | * [External Implementations](External_Implementations.md) - need an Ada or Typescript library? 8 | * [Frequently Asked Questions (FAQ)](FAQ.md) 9 | * [Who is using Plus Codes?](Reference/Plus_Code_Users.md) 10 | 11 | ### Specification and technical implementation 12 | 13 | * [Open Location Code Overview](Specification/olc_definition.adoc) 14 | * [Open Location Code Specification](Specification/specification.md) 15 | * [Guidance for Shortening Codes](Specification/Short_Code_Guidance.md) 16 | * [Open Location Code Reference API](Specification/API.md) 17 | * [Plus Codes and Open Location Code Naming Guidelines](Specification/Naming_Guidelines.md) 18 | 19 | ### Technical 20 | 21 | * [An Evaluation of Location Encoding Systems](Reference/comparison.adoc) 22 | * [Supporting Plus Codes in GIS software](Reference/GIS_Software.md) 23 | * [Supporting Plus Codes in an app](Reference/App_Developers.md) 24 | 25 | ### Tools 26 | 27 | * [Field Data Collection Practices](Reference/Field_Collection_Data_Practices.md) 28 | * [https://plus.codes API Reference](Reference/plus.codes_Website_API.md) 29 | * [Using Plus Codes in Spreadsheets](Reference/Using_Spreadsheets.md) 30 | -------------------------------------------------------------------------------- /Documentation/Reference/GIS_Software.md: -------------------------------------------------------------------------------- 1 | # Plus Codes in GIS software 2 | 3 | This page provides information about using Plus Codes in GIS software. 4 | 5 | ## Tile Service 6 | 7 | If you want to visualise the Plus Codes grid, you can use the [grid service](https://grid.plus.codes) to fetch the grid tiles. 8 | 9 | This is a shared service, and it may rate limit you. 10 | If you need to use the grid heavily, you can start your own [tile_server](https://github.com/google/open-location-code/blob/main/tile_server). 11 | 12 | The tile service provides GeoJSON objects, one per Plus Codes square, or PNG images that can be added as an overlay. 13 | 14 | ## Software 15 | 16 | ### QGIS 17 | 18 | | precision level | intervals in degrees | 19 | |-----|-----| 20 | | 10 | 0.000125 | 21 | | 8 | 0.0025 | 22 | | 6 | 0.05 | 23 | | 4 | 1 | 24 | | 2 | 20 | 25 | 26 | We can generate the grid lines in QGIS. 27 | 28 | Just make sure your starting lat-long values are an exact multiple of the interval values for your chosen precision level you want. 29 | 30 | Example : Creating a grid with precision 6 : starting latitude cannot be 16.4563. 31 | Change it to 16.45 or 16.50 so that when you divide it by 0.05 it gives you an integer answer. 32 | 33 | In QGIS, you can generate a grid by clicking in the top Menu: Vector > Research tools > Vector Grid 34 | 35 | * Grid extent (xmin,xmax,ymin,ymax): 78.1,79,16.9,18.2 (in my example, a city in India) 36 | * Set both lat and lon interval as per the precision level you want. 37 | So for precision level 6, enter 0.05 for both. 38 | * You can set output as lines or polygons, your choice. 39 | Lines make simpler and smaller shapefiles. 40 | * And that should generate the grid for you. 41 | You can save that layer as a shapefile in any format. 42 | 43 | Note that this will not put any information about the Plus Codes in your grid's metadata. 44 | They're just lines/boxes. 45 | 46 | But if you make polygons, then I can think of a roundabout way of adding Plus Code values to those polygons (I have not done this myself yet): 47 | 48 | * Generate a centroid layer (Vector > Geometry Tools > Polygon Centroid) from the grid-polygons layer. 49 | This will place points inside each grid box. (in a new points layer.) 50 | * Install "Lat Lon Tools" plugin. 51 | * That plugin can generate Plus Codes from points. 52 | So run it on the centroid layer you made. 53 | * (And this I can't quite figure out yet) Figure out a way to move the meta field from the centroid layer to the grid polygons layer. 54 | 55 | There is a plugin for QGIS, [Lat Lon tools](https://github.com/NationalSecurityAgency/qgis-latlontools-plugin). 56 | 57 | -------------------------------------------------------------------------------- /Documentation/Reference/Plus_Code_Users.md: -------------------------------------------------------------------------------- 1 | # Where Are Plus Codes Being Used? 2 | 3 | This page lists the sites, apps and organisations that support Plus Codes / Open Location Code. 4 | 5 | # Organisations 6 | 7 | * [Correios de Cabo Verde](correios.cv) (August 2016). 8 | Support Plus Codes for postal delivery. 9 | 10 | # Apps and sites 11 | 12 | * Google (Search, Maps) 13 | * Search support for global and local codes (early 2016). 14 | * Display of global codes in [Android](https://play.google.com/store/apps/details?id=com.google.android.apps.maps) and [iOS](https://itunes.apple.com/app/id585027354) maps (September 2016). 15 | * [Assistant integration](https://assistant.google.com/services/a/uid/000000706b4e2cf1?hl=en) (early 2018) 16 | * [mapy.cz](mapy.cz) (mid 2016) Search support for global codes. 17 | * www.waze.com (early 2018?) Search support for Plus Code addresses (global and local) 18 | * www.locusmap.eu (early 2018) Supports using global codes to set the map location 19 | * [OSMAnd](https://osmand.net/) OpenStreetMap based offline mobile maps and navigation - Supports displaying OLC as the "coordinates" of any point on earth. 20 | * [QGIS](https://qgis.org/) via [Lat Lon Tools](https://plugins.qgis.org/plugins/latlontools/) plug in - good support, points to olc, olc to points 21 | * [nicesi.com](https://nicesi.com/) (early 2019) Nicesi Location API support for global codes in [reverse geocoding service](https://nicesi.com/doc/#reverse-geocoding) -------------------------------------------------------------------------------- /Documentation/Reference/Using_Spreadsheets.md: -------------------------------------------------------------------------------- 1 | # Using Plus Codes in Spreadsheets 2 | 3 | Being able to work with Plus Codes in spreadsheets is, for most people, probably the easiest method to work with them in bulk. 4 | 5 | This page explains how you can access the Open Location Code functions from Excel, LibreOffice or Google Spreadsheets. 6 | 7 | ## Google Sheets 8 | 9 | There is an [add-on for Google Sheets](https://gsuite.google.com/marketplace/app/plus_codes/604254879289) that allows you to create Plus Codes from latitude and longitude coordinates, and to decode them as well. 10 | 11 | The [Google Maps YouTube channel](https://www.youtube.com/@googlemaps) has some [videos showing how to install and use the add-on](https://www.youtube.com/playlist?list=PLcRbp4LqBpwE5ofG2MN08D_4DJAk9gZBI). 12 | 13 | ## Excel/LibreOffice 14 | 15 | VBA script and instructions are checked into github [here](https://github.com/google/open-location-code/blob/main/visualbasic). 16 | 17 | LibreOffice does sometimes have problems. 18 | This may be due to slightly unreliable VBA support in some versions. 19 | 20 | -------------------------------------------------------------------------------- /Documentation/Specification/Naming_Guidelines.md: -------------------------------------------------------------------------------- 1 | # Plus Codes and Open Location Code Naming Guidelines 2 | 3 | ## Why have guidelines? 4 | 5 | These guidelines are offered so that people see a consistent reference to the codes and technology. 6 | This will make it easier for them to understand what they are being shown or asked for. 7 | 8 | We have chosen two names - one for the technology and one for the actual codes. 9 | The name for the codes reflects and reinforces the importance of the plus symbol, which is how the codes can be recognised. 10 | 11 | ## Plus Codes 12 | 13 | When referring to Plus Codes in English, the **only** term that should be used is "Plus Code", in Title Case. 14 | 15 | Some examples of usage are: 16 | * "My Plus Code is CX37+M9." 17 | * "I gave your Plus Code to the cab driver and he found the way without any problems." 18 | * "Will the postcard arrive if I put your Plus Code as the address?" 19 | * "Enter your Plus Code or street address here." 20 | 21 | ### Global and local codes 22 | 23 | Codes that can be decoded to a lat/lng on their own, e.g. 796RWF8Q+WF are referred to as global codes. 24 | 25 | The shortened codes, e.g., WF8Q+WF are referred to as local codes, because they work within a local area. 26 | 27 | ## Open Location Code 28 | 29 | When discussing with organisations or developers, refer to the library, algorithm and technology as "Open Location Code". 30 | This can be abbreviated to OLC, is capitalised, and shouldn't be translated. 31 | 32 | It shouldn't be used to refer to the actual codes - don't say "Open Location Codes" for example. 33 | 34 | ## Summary 35 | 36 | Having consistent names and presentation will make it easier for people to recognise what is meant, and make it easier for them to use and benefit from the project. 37 | 38 | -------------------------------------------------------------------------------- /Documentation/images/code_areas.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/open-location-code/3cf9f806af4d1c47388badea615064544cec94f5/Documentation/images/code_areas.png -------------------------------------------------------------------------------- /Documentation/images/geohash36_grid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/open-location-code/3cf9f806af4d1c47388badea615064544cec94f5/Documentation/images/geohash36_grid.png -------------------------------------------------------------------------------- /Documentation/images/olc_10_character.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/open-location-code/3cf9f806af4d1c47388badea615064544cec94f5/Documentation/images/olc_10_character.png -------------------------------------------------------------------------------- /Documentation/images/olc_11_grid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/open-location-code/3cf9f806af4d1c47388badea615064544cec94f5/Documentation/images/olc_11_grid.png -------------------------------------------------------------------------------- /Documentation/images/openpostcode_grid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/open-location-code/3cf9f806af4d1c47388badea615064544cec94f5/Documentation/images/openpostcode_grid.png -------------------------------------------------------------------------------- /MODULE.bazel: -------------------------------------------------------------------------------- 1 | module( 2 | name = "openlocationcode", 3 | version = "1.0", 4 | ) 5 | 6 | # bazel-skylib required by #@io_bazel_rules_closure. 7 | # https://github.com/bazelbuild/bazel-skylib 8 | bazel_dep(name = "bazel_skylib", version = "1.7.1") 9 | 10 | # googletest required by c/BUILD. 11 | # https://github.com/google/googletest 12 | bazel_dep(name = "googletest", version = "1.15.2") 13 | 14 | -------------------------------------------------------------------------------- /WORKSPACE: -------------------------------------------------------------------------------- 1 | # Workspace configuration for Bazel build tools. 2 | 3 | # TODO: #642 -- Remove once io_bazel_rules_closure supports Bazel module configuration. 4 | workspace(name = "openlocationcode") 5 | 6 | load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") 7 | 8 | http_archive( 9 | name = "io_bazel_rules_closure", 10 | integrity = "sha256-EvEWnr54L4Yx/LjagaoSuhkviVKHW0oejyDEn8bhAiM=", 11 | strip_prefix = "rules_closure-0.14.0", 12 | urls = [ 13 | "https://mirror.bazel.build/github.com/bazelbuild/rules_closure/archive/0.14.0.tar.gz", 14 | "https://github.com/bazelbuild/rules_closure/archive/0.14.0.tar.gz", 15 | ], 16 | ) 17 | load("@io_bazel_rules_closure//closure:repositories.bzl", "rules_closure_dependencies", "rules_closure_toolchains") 18 | rules_closure_dependencies() 19 | rules_closure_toolchains() 20 | -------------------------------------------------------------------------------- /android_demo/.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | */build/ 3 | 4 | # Local configuration file (sdk path, etc) 5 | local.properties 6 | 7 | # IntelliJ configuration file 8 | android/android.iml 9 | 10 | # Gradle generated files 11 | .gradle/ 12 | 13 | # Signing files 14 | .signing/ 15 | 16 | # User-specific configurations 17 | .idea/* 18 | 19 | # OS-specific files 20 | .DS_Store 21 | ._* 22 | .Trashes 23 | -------------------------------------------------------------------------------- /android_demo/README.md: -------------------------------------------------------------------------------- 1 | Android Open Location Code Demonstrator 2 | ======================================= 3 | 4 | This is the source code for an Android app that uses 5 | [Open Location Code](https://maps.google.com/pluscodes/) and displays them on a 6 | map. 7 | 8 | It displays the current location, computes the code for that location, and uses 9 | a bundled set of place names to shorten the code to a more convenient form. 10 | Currently all the place names exist within Cape Verde. Other place names can be 11 | added to the source. 12 | 13 | Using a bundled set of places means that determining the current address, and 14 | locating an entered address will work offline. 15 | 16 | This project has been structured to be used with 17 | [Android Studio](https://developer.android.com/studio/index.html). 18 | 19 | An Open Location Code JAR file is in the directory `android/libs`. If the core library 20 | is updated, you will need to update this JAR file. 21 | -------------------------------------------------------------------------------- /android_demo/android/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'com.neenbedankt.android-apt' 3 | 4 | android { 5 | compileSdkVersion rootProject.compileSdkVersion 6 | buildToolsVersion rootProject.buildToolsVersion 7 | 8 | defaultConfig { 9 | applicationId "com.openlocationcode.android" 10 | minSdkVersion rootProject.minSdkVersion 11 | targetSdkVersion rootProject.targetSdkVersion 12 | versionCode 1 13 | versionName "1.0" 14 | } 15 | buildTypes { 16 | release { 17 | minifyEnabled false 18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 19 | } 20 | } 21 | } 22 | 23 | dependencies { 24 | compile fileTree(dir: 'libs', include: ['*.jar']) 25 | testCompile 'junit:junit:4.12' 26 | compile "com.android.support:appcompat-v7:$rootProject.supportLibraryVersion" 27 | compile "com.android.support:design:$rootProject.supportLibraryVersion" 28 | 29 | // Dagger dependencies 30 | apt "com.google.dagger:dagger-compiler:$rootProject.daggerVersion" 31 | provided 'org.glassfish:javax.annotation:10.0-b28' 32 | compile "com.google.dagger:dagger:$rootProject.daggerVersion" 33 | 34 | compile "com.android.volley:volley:$rootProject.volleyVersion" 35 | 36 | compile "com.google.auto.factory:auto-factory:$rootProject.autoFactoryVersion" 37 | 38 | 39 | compile "com.google.android.gms:play-services-location:$rootProject.gmsVersion" 40 | compile "com.google.android.gms:play-services-base:$rootProject.gmsVersion" 41 | } 42 | -------------------------------------------------------------------------------- /android_demo/android/libs/README.md: -------------------------------------------------------------------------------- 1 | Building the Open Location Code JAR file 2 | == 3 | 4 | Using the source in the 5 | [Java](https://github.com/google/open-location-code/blob/master/java/com/google/openlocationcode/OpenLocationCode.java) 6 | implementation, build a JAR file and put it in this location. 7 | 8 | -- 9 | 10 | Assuming you've downloaded this repository locally: 11 | 12 | ``` 13 | cd open-location-code-master/java 14 | javac com/google/openlocationcode/OpenLocationCode.java 15 | jar -cfM ./openlocationcode.jar com/google/openlocationcode/OpenLocationCode\$CodeArea.class com/google/openlocationcode/OpenLocationCode.class 16 | ``` 17 | 18 | The `.jar` file is in the `open-location-code-master/java` directory 19 | 20 | If working with Android Studio, add `openlocationcode.jar` to `/{PROJECT_NAME}/{APP}/libs` *(you may need to create the `/libs` folder)* 21 | 22 | Why don't we include a JAR file here? 23 | -- 24 | 25 | Basically, we want to make sure that we don't fix a bug in the Java implementation and forget to 26 | update this JAR file. 27 | 28 | Why don't we have a Maven repository? 29 | -- 30 | 31 | So far, we've only had one request. If you would like to be able to pull the library via Maven, 32 | file an issue and we'll consider it. 33 | -------------------------------------------------------------------------------- /android_demo/android/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can edit the include path and order by changing the proguardFiles 3 | # directive in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # Add any project specific keep options here: 9 | 10 | # If your project uses WebView with JS, uncomment the following 11 | # and specify the fully qualified class name to the JavaScript interface 12 | # class: 13 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 14 | # public *; 15 | #} 16 | -------------------------------------------------------------------------------- /android_demo/android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 16 | 20 | 21 | 22 | 23 | 24 | 25 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /android_demo/android/src/main/java/com/openlocationcode/android/code/CodeContract.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.openlocationcode.android.code; 18 | 19 | import com.openlocationcode.android.direction.Direction; 20 | 21 | import com.google.openlocationcode.OpenLocationCode; 22 | 23 | /** 24 | * The contract for the functionality displaying the code, in {@link 25 | * com.openlocationcode.android.main.MainActivity}. 26 | */ 27 | public interface CodeContract { 28 | 29 | interface View { 30 | 31 | /** 32 | * Implements displaying the {@code code}. 33 | */ 34 | void displayCode(OpenLocationCode code); 35 | } 36 | 37 | interface ActionsListener { 38 | 39 | /** 40 | * Call this when the code is requested for a new location (eg when the map is dragged). 41 | * 42 | * @param latitude 43 | * @param longitude 44 | * @param isCurrent 45 | * @return 46 | */ 47 | Direction codeLocationUpdated(double latitude, double longitude, boolean isCurrent); 48 | 49 | /** 50 | * @return the code currently displayed to the user. 51 | */ 52 | OpenLocationCode getCurrentOpenLocationCode(); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /android_demo/android/src/main/java/com/openlocationcode/android/current/GoogleApiModule.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.openlocationcode.android.current; 18 | 19 | import com.google.android.gms.common.GoogleApiAvailability; 20 | import com.google.android.gms.common.api.GoogleApiClient; 21 | import com.google.android.gms.location.FusedLocationProviderApi; 22 | import com.google.android.gms.location.LocationServices; 23 | 24 | import android.content.Context; 25 | import android.hardware.SensorManager; 26 | import android.location.LocationManager; 27 | import android.view.Display; 28 | import android.view.WindowManager; 29 | 30 | import dagger.Module; 31 | import dagger.Provides; 32 | 33 | import javax.inject.Singleton; 34 | 35 | @Module 36 | public class GoogleApiModule { 37 | 38 | private final Context mContext; 39 | 40 | public GoogleApiModule(Context context) { 41 | this.mContext = context; 42 | } 43 | 44 | @Provides 45 | @Singleton 46 | public GoogleApiClient provideGoogleApiClient() { 47 | return new GoogleApiClient.Builder(mContext).addApi(LocationServices.API).build(); 48 | } 49 | 50 | @Provides 51 | @Singleton 52 | public GoogleApiAvailability provideGoogleApiAvailability() { 53 | return GoogleApiAvailability.getInstance(); 54 | } 55 | 56 | @SuppressWarnings("SameReturnValue") 57 | @Provides 58 | @Singleton 59 | public FusedLocationProviderApi provideFusedLocationProviderApi() { 60 | return LocationServices.FusedLocationApi; 61 | } 62 | 63 | @Provides 64 | @Singleton 65 | public LocationManager provideLocationManager() { 66 | return (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE); 67 | } 68 | 69 | @Provides 70 | @Singleton 71 | public SensorManager provideSensorManager() { 72 | return (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE); 73 | } 74 | 75 | @Provides 76 | @Singleton 77 | public Display provideDisplayManager() { 78 | return ((WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE)) 79 | .getDefaultDisplay(); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /android_demo/android/src/main/java/com/openlocationcode/android/current/LocationProviderFactoryComponent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.openlocationcode.android.current; 18 | 19 | import dagger.Component; 20 | 21 | import javax.inject.Singleton; 22 | 23 | @Component(modules = {GoogleApiModule.class}) 24 | @Singleton 25 | public interface LocationProviderFactoryComponent { 26 | LocationProviderFactory locationProviderFactory(); 27 | } 28 | -------------------------------------------------------------------------------- /android_demo/android/src/main/java/com/openlocationcode/android/direction/Direction.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.openlocationcode.android.direction; 18 | 19 | import com.google.openlocationcode.OpenLocationCode; 20 | 21 | import java.util.Locale; 22 | 23 | /** 24 | * Immutable model representing the direction between two given locations. It contains the distance, 25 | * the initial and the final bearing. To understand the meaning of initial and final bearing, refer 26 | * to http://www.onlineconversion.com/map_greatcircle_bearings.htm. 27 | */ 28 | public class Direction { 29 | 30 | private final int mDistanceInMeter; 31 | 32 | private final float mInitialBearing; 33 | 34 | private final OpenLocationCode mFromCode; 35 | 36 | private final OpenLocationCode mToCode; 37 | 38 | public Direction( 39 | OpenLocationCode fromCode, 40 | OpenLocationCode toCode, 41 | float distanceInMeter, 42 | float initialBearing) { 43 | mDistanceInMeter = (int) distanceInMeter; 44 | mInitialBearing = initialBearing; 45 | mFromCode = fromCode; 46 | mToCode = toCode; 47 | } 48 | 49 | /** 50 | * @return Bearing in degrees East of true North. 51 | */ 52 | public float getInitialBearing() { 53 | return mInitialBearing; 54 | } 55 | 56 | /** 57 | * @return Distance in meter. 58 | */ 59 | public int getDistance() { 60 | return mDistanceInMeter; 61 | } 62 | 63 | /** 64 | * @return The code representing the origin location. 65 | */ 66 | public OpenLocationCode getFromCode() { 67 | return mFromCode; 68 | } 69 | 70 | /** 71 | * @return The code representing the destination location. 72 | */ 73 | public OpenLocationCode getToCode() { 74 | return mToCode; 75 | } 76 | 77 | @Override 78 | public String toString() { 79 | return String.format( 80 | Locale.US, 81 | "Direction from code %s to %s, distance %d, initial bearing %f", 82 | mFromCode, 83 | mToCode, 84 | mDistanceInMeter, 85 | mInitialBearing); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /android_demo/android/src/main/java/com/openlocationcode/android/direction/DirectionContract.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.openlocationcode.android.direction; 18 | 19 | /** 20 | * The contract for the direction feature. The direction shows the direction (bearing) and the 21 | * distance of a code, compared to the user current location. 22 | */ 23 | public interface DirectionContract { 24 | 25 | interface View { 26 | 27 | void showDirection(float degreesFromNorth); 28 | 29 | void showDistance(int distanceInMeters); 30 | } 31 | 32 | interface ActionsListener { 33 | 34 | /** 35 | * Call this when the user current location or the code currently shown to the user is 36 | * updated. 37 | */ 38 | void directionUpdated(Direction direction); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /android_demo/android/src/main/java/com/openlocationcode/android/direction/DirectionPresenter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.openlocationcode.android.direction; 18 | 19 | public class DirectionPresenter implements DirectionContract.ActionsListener { 20 | 21 | private final DirectionView mView; 22 | 23 | public DirectionPresenter(DirectionView view) { 24 | mView = view; 25 | } 26 | 27 | @Override 28 | public void directionUpdated(Direction direction) { 29 | mView.showDirection(direction.getInitialBearing()); 30 | mView.showDistance(direction.getDistance()); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /android_demo/android/src/main/java/com/openlocationcode/android/direction/DirectionUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.openlocationcode.android.direction; 18 | 19 | import android.location.Location; 20 | 21 | import com.google.openlocationcode.OpenLocationCode; 22 | import com.google.openlocationcode.OpenLocationCode.CodeArea; 23 | 24 | import com.openlocationcode.android.code.OpenLocationCodeUtil; 25 | 26 | /** 27 | * Util functions related to direction. 28 | */ 29 | public class DirectionUtil { 30 | 31 | /** 32 | * This computes a direction between {@code fromLocation} and a 33 | * {@code destinationCode}. The computation is done using 34 | * {@link Location#distanceBetween(double, double, double, double, float[])}. 35 | * 36 | * @param fromLocation The user position. 37 | * @param destinationCode The code to compute the direction to. 38 | * @return the {@link Direction} 39 | */ 40 | public static Direction getDirection(Location fromLocation, OpenLocationCode destinationCode) { 41 | CodeArea destinationArea = destinationCode.decode(); 42 | double toLatitude = destinationArea.getCenterLatitude(); 43 | double toLongitude = destinationArea.getCenterLongitude(); 44 | float[] results = new float[3]; 45 | Location.distanceBetween( 46 | fromLocation.getLatitude(), 47 | fromLocation.getLongitude(), 48 | toLatitude, 49 | toLongitude, 50 | results); 51 | 52 | // The device bearing in the location object is 0-360, the value returned from 53 | // distanceBetween is -180 to 180. Adjust the device bearing to be in the same range. 54 | float deviceBearing = fromLocation.getBearing(); 55 | if (deviceBearing > 180) { 56 | deviceBearing = deviceBearing - 360; 57 | } 58 | 59 | // Compensate the initial bearing for the device bearing. 60 | results[1] = results[1] - deviceBearing; 61 | if (results[1] > 180) { 62 | results[1] = -360 + results[1]; 63 | } else if (results[1] < -180) { 64 | results[1] = 360 + results[1]; 65 | } 66 | return new Direction( 67 | OpenLocationCodeUtil.createOpenLocationCode( 68 | fromLocation.getLatitude(), fromLocation.getLongitude()), 69 | destinationCode, 70 | results[0], 71 | results[1]); 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /android_demo/android/src/main/java/com/openlocationcode/android/main/MainFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.openlocationcode.android.main; 18 | 19 | import android.app.Fragment; 20 | import android.os.Bundle; 21 | import android.view.LayoutInflater; 22 | import android.view.View; 23 | import android.view.ViewGroup; 24 | 25 | import com.openlocationcode.android.R; 26 | 27 | /** 28 | * A placeholder fragment containing a simple view. 29 | */ 30 | public class MainFragment extends Fragment { 31 | 32 | public MainFragment() { 33 | } 34 | 35 | @Override 36 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 37 | Bundle savedInstanceState) { 38 | 39 | return inflater.inflate(R.layout.main_frag, container, false); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /android_demo/android/src/main/java/com/openlocationcode/android/map/MapContract.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.openlocationcode.android.map; 18 | 19 | import android.location.Location; 20 | 21 | import com.google.openlocationcode.OpenLocationCode; 22 | import com.google.android.gms.maps.model.CameraPosition; 23 | 24 | 25 | /** 26 | * The contract for the map feature. 27 | */ 28 | public interface MapContract { 29 | 30 | interface View { 31 | 32 | void moveMapToLocation(OpenLocationCode code); 33 | 34 | void drawCodeArea(OpenLocationCode code); 35 | 36 | void showSatelliteView(); 37 | 38 | void showRoadView(); 39 | 40 | void setListener(ActionsListener listener); 41 | 42 | CameraPosition getCameraPosition(); 43 | 44 | void setCameraPosition(double latitude, double longitude, float zoom); 45 | 46 | void stopUpdateCodeOnDrag(); 47 | 48 | void startUpdateCodeOnDrag(); 49 | } 50 | 51 | interface ActionsListener { 52 | 53 | void mapChanged(double latitude, double longitude); 54 | 55 | void requestSatelliteView(); 56 | 57 | void requestRoadView(); 58 | 59 | void moveMapToLocation(Location location); 60 | 61 | CameraPosition getMapCameraPosition(); 62 | 63 | void setMapCameraPosition(double latitude, double longitude, float zoom); 64 | 65 | /** 66 | * Call this to stop updating the code feature on dragging the map. This is used by the 67 | * search feature, ie make sure the search result is shown and not cancelled by dragging 68 | * the map. 69 | */ 70 | void stopUpdateCodeOnDrag(); 71 | 72 | /** 73 | * Call this to start updating the code feature on dragging the map, eg when search is 74 | * cancelled. 75 | */ 76 | void startUpdateCodeOnDrag(); 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /android_demo/android/src/main/java/com/openlocationcode/android/search/AutoCompleteEditor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.openlocationcode.android.search; 18 | 19 | import android.content.Context; 20 | import android.util.AttributeSet; 21 | import android.view.KeyEvent; 22 | import android.widget.AutoCompleteTextView; 23 | 24 | /** 25 | * Super class of AutoCompleteTextView that allows detection of the soft keyboard dismissal. 26 | */ 27 | public class AutoCompleteEditor extends AutoCompleteTextView { 28 | 29 | private com.openlocationcode.android.search.SearchContract.SourceView imeBackListener; 30 | 31 | public AutoCompleteEditor(Context context) { 32 | super(context); 33 | } 34 | 35 | public AutoCompleteEditor(Context context, AttributeSet attrs) { 36 | super(context, attrs); 37 | } 38 | 39 | public void setImeBackListener(SearchContract.SourceView imeBackListener) { 40 | this.imeBackListener = imeBackListener; 41 | } 42 | 43 | @Override 44 | public boolean onKeyPreIme(int keyCode, KeyEvent event) { 45 | if (event.getKeyCode() == KeyEvent.KEYCODE_BACK 46 | && event.getAction() == KeyEvent.ACTION_UP) { 47 | if (imeBackListener != null) { 48 | imeBackListener.imeBackHandler(); 49 | } 50 | } 51 | 52 | return super.onKeyPreIme(keyCode, event); 53 | } 54 | } 55 | 56 | -------------------------------------------------------------------------------- /android_demo/android/src/main/java/com/openlocationcode/android/search/SearchContract.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.openlocationcode.android.search; 18 | 19 | import com.google.openlocationcode.OpenLocationCode; 20 | 21 | import java.util.List; 22 | 23 | /** 24 | * The contract for the search functionality. 25 | */ 26 | public interface SearchContract { 27 | 28 | /** 29 | * The contract for a view allowing the user to enter search criteria. 30 | */ 31 | interface SourceView { 32 | 33 | void showInvalidCode(); 34 | 35 | void showEmptyCode(); 36 | 37 | void setActionsListener(ActionsListener listener); 38 | 39 | void showSuggestions(List suggestions); 40 | 41 | void imeBackHandler(); 42 | 43 | void setText(String text); 44 | 45 | } 46 | 47 | /** 48 | * The contract for a view displaying the result of the search. 49 | */ 50 | interface TargetView { 51 | 52 | void showSearchCode(OpenLocationCode code); 53 | 54 | } 55 | 56 | interface ActionsListener { 57 | 58 | boolean searchCode(String code); 59 | 60 | void getSuggestions(String code); 61 | 62 | void setSearchText(String text); 63 | 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /android_demo/android/src/main/res/drawable-hdpi/ic_action_maps_navigation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/open-location-code/3cf9f806af4d1c47388badea615064544cec94f5/android_demo/android/src/main/res/drawable-hdpi/ic_action_maps_navigation.png -------------------------------------------------------------------------------- /android_demo/android/src/main/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/open-location-code/3cf9f806af4d1c47388badea615064544cec94f5/android_demo/android/src/main/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android_demo/android/src/main/res/drawable-hdpi/map_center.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/open-location-code/3cf9f806af4d1c47388badea615064544cec94f5/android_demo/android/src/main/res/drawable-hdpi/map_center.png -------------------------------------------------------------------------------- /android_demo/android/src/main/res/drawable-hdpi/welcome_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/open-location-code/3cf9f806af4d1c47388badea615064544cec94f5/android_demo/android/src/main/res/drawable-hdpi/welcome_image.png -------------------------------------------------------------------------------- /android_demo/android/src/main/res/drawable-ldpi/map_center.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/open-location-code/3cf9f806af4d1c47388badea615064544cec94f5/android_demo/android/src/main/res/drawable-ldpi/map_center.png -------------------------------------------------------------------------------- /android_demo/android/src/main/res/drawable-mdpi/ic_action_maps_navigation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/open-location-code/3cf9f806af4d1c47388badea615064544cec94f5/android_demo/android/src/main/res/drawable-mdpi/ic_action_maps_navigation.png -------------------------------------------------------------------------------- /android_demo/android/src/main/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/open-location-code/3cf9f806af4d1c47388badea615064544cec94f5/android_demo/android/src/main/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android_demo/android/src/main/res/drawable-mdpi/map_center.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/open-location-code/3cf9f806af4d1c47388badea615064544cec94f5/android_demo/android/src/main/res/drawable-mdpi/map_center.png -------------------------------------------------------------------------------- /android_demo/android/src/main/res/drawable-xhdpi/ic_action_maps_navigation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/open-location-code/3cf9f806af4d1c47388badea615064544cec94f5/android_demo/android/src/main/res/drawable-xhdpi/ic_action_maps_navigation.png -------------------------------------------------------------------------------- /android_demo/android/src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/open-location-code/3cf9f806af4d1c47388badea615064544cec94f5/android_demo/android/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android_demo/android/src/main/res/drawable-xhdpi/map_center.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/open-location-code/3cf9f806af4d1c47388badea615064544cec94f5/android_demo/android/src/main/res/drawable-xhdpi/map_center.png -------------------------------------------------------------------------------- /android_demo/android/src/main/res/drawable-xhdpi/welcome_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/open-location-code/3cf9f806af4d1c47388badea615064544cec94f5/android_demo/android/src/main/res/drawable-xhdpi/welcome_image.png -------------------------------------------------------------------------------- /android_demo/android/src/main/res/drawable-xxhdpi/ic_action_maps_navigation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/open-location-code/3cf9f806af4d1c47388badea615064544cec94f5/android_demo/android/src/main/res/drawable-xxhdpi/ic_action_maps_navigation.png -------------------------------------------------------------------------------- /android_demo/android/src/main/res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/open-location-code/3cf9f806af4d1c47388badea615064544cec94f5/android_demo/android/src/main/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android_demo/android/src/main/res/drawable-xxhdpi/map_center.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/open-location-code/3cf9f806af4d1c47388badea615064544cec94f5/android_demo/android/src/main/res/drawable-xxhdpi/map_center.png -------------------------------------------------------------------------------- /android_demo/android/src/main/res/drawable-xxhdpi/welcome_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/open-location-code/3cf9f806af4d1c47388badea615064544cec94f5/android_demo/android/src/main/res/drawable-xxhdpi/welcome_image.png -------------------------------------------------------------------------------- /android_demo/android/src/main/res/drawable-xxxhdpi/ic_action_maps_navigation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/open-location-code/3cf9f806af4d1c47388badea615064544cec94f5/android_demo/android/src/main/res/drawable-xxxhdpi/ic_action_maps_navigation.png -------------------------------------------------------------------------------- /android_demo/android/src/main/res/drawable-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/open-location-code/3cf9f806af4d1c47388badea615064544cec94f5/android_demo/android/src/main/res/drawable-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android_demo/android/src/main/res/drawable-xxxhdpi/map_center.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/open-location-code/3cf9f806af4d1c47388badea615064544cec94f5/android_demo/android/src/main/res/drawable-xxxhdpi/map_center.png -------------------------------------------------------------------------------- /android_demo/android/src/main/res/drawable/map_satellite_button.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /android_demo/android/src/main/res/drawable/rounded_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /android_demo/android/src/main/res/drawable/search_button.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /android_demo/android/src/main/res/layout/direction.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 23 | 24 | 31 | 32 | 37 | 38 | -------------------------------------------------------------------------------- /android_demo/android/src/main/res/layout/main_act.xml: -------------------------------------------------------------------------------- 1 | 16 | 22 | 23 | 29 | 30 | -------------------------------------------------------------------------------- /android_demo/android/src/main/res/layout/main_frag.xml: -------------------------------------------------------------------------------- 1 | 16 | 19 | 20 | 27 | 28 | 36 | 37 | 42 | 52 | 53 | 54 | 55 | 60 | 61 | 65 | 66 | -------------------------------------------------------------------------------- /android_demo/android/src/main/res/menu/share_menu.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 20 | 23 | 24 | -------------------------------------------------------------------------------- /android_demo/android/src/main/res/values-pt/strings.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | OLC Demo 18 | OLC Demo 19 | 20 | Obter o seu código 21 | 22 | Sem código para mostrar 23 | A carregar a sua localização 24 | Não identificámos a sua localização 25 | 26 | Partilhar através de… 27 | Partilhar… 28 | Guardar em contactos 29 | 30 | localidade desconhecida 31 | 32 | %d m 33 | %.1f km 34 | %.0f km 35 | 36 | Introduza um código 37 | Código não encontrado 38 | Introduza um código para procurar 39 | 40 | Descubra o seu OLC 41 | Descubra o OLC (Open Location Code) de sua casa. OLC são livres e funciona com o Google Maps. 42 | 43 | Navegar da sua localização ao código 44 | Partilhar código ou guardar em contactos 45 | Camada de satélite de alternância 46 | Mostrar a sua localização actual 47 | Anular pesquisa 48 | 49 | -------------------------------------------------------------------------------- /android_demo/android/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | #A0CC0000 18 | #A0FF0000 19 | #CCCCCC 20 | #333 21 | #AAA 22 | #F16054 23 | #FFFFFF 24 | 25 | -------------------------------------------------------------------------------- /android_demo/android/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 14sp 18 | 18sp 19 | 20 | 4dp 21 | 2dp 22 | 23 | 16dp 24 | 104dp 25 | 26 | 60dp 27 | 8dp 28 | 29 | 8dp 30 | 31 | 48dp 32 | 16dp 33 | 16dp 34 | 8dp 35 | 8dp 36 | 16dp 37 | 2dp 38 | 10dp 39 | 40 | 90dp 41 | 20dp 42 | 8dp 43 | 44 | 8dp 45 | 24dp 46 | 32dp 47 | 48 | -------------------------------------------------------------------------------- /android_demo/android/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | OLC Demo 18 | OLC Demo 19 | 20 | Get your code 21 | 22 | No code to display 23 | Loading your location 24 | Can\'t determine your location 25 | 26 | Share via 27 | Share… 28 | Save to contact 29 | 30 | unknown locality 31 | 32 | %d m 33 | %.1f km 34 | %.0f km 35 | 36 | Look up a code 37 | Code not found 38 | Enter a code to search 39 | 40 | Discover your OLC 41 | Discover the Plus Code of your home.\nPlus Codes are free and 42 | work with Google Maps. 43 | 44 | Navigate from your location to the code 45 | Share the code or add to a contact 46 | Toggle satellite layer 47 | Show your current location 48 | Cancel search 49 | 50 | -------------------------------------------------------------------------------- /android_demo/android/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | 19 | 26 | 27 | 30 | 31 | 34 | 35 | 36 | 40 | 41 | 42 | 48 | 49 | 53 | 54 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /android_demo/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | buildscript { 3 | repositories { 4 | jcenter() 5 | } 6 | dependencies { 7 | classpath 'com.android.tools.build:gradle:2.1.0' 8 | 9 | // Better IDE support for annotations (so Android Studio interacts better with Dagger) 10 | classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' 11 | 12 | // NOTE: Do not place your application dependencies here; they belong 13 | // in the individual module build.gradle files 14 | } 15 | } 16 | 17 | allprojects { 18 | repositories { 19 | jcenter() 20 | } 21 | } 22 | 23 | task clean(type: Delete) { 24 | delete rootProject.buildDir 25 | } 26 | 27 | // Define versions in a single place 28 | ext { 29 | // Sdk and tools 30 | minSdkVersion = 14 31 | targetSdkVersion = 22 32 | compileSdkVersion = 23 33 | buildToolsVersion = '23.0.3' 34 | 35 | // App dependencies 36 | supportLibraryVersion = '23.3.0' 37 | guavaVersion = '18.0' 38 | junitVersion = '4.12' 39 | mockitoVersion = '1.10.19' 40 | powerMockito = '1.6.2' 41 | hamcrestVersion = '1.3' 42 | runnerVersion = '0.4.1' 43 | rulesVersion = '0.4.1' 44 | espressoVersion = '2.2.1' 45 | daggerVersion = '2.0' 46 | autoFactoryVersion = '1.0-beta3' 47 | volleyVersion = '1.0.0' 48 | gmsVersion = '8.4.0' 49 | } -------------------------------------------------------------------------------- /android_demo/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/open-location-code/3cf9f806af4d1c47388badea615064544cec94f5/android_demo/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /android_demo/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Oct 21 11:34:03 PDT 2015 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip 7 | -------------------------------------------------------------------------------- /android_demo/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /android_demo/openlocationcode_android.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /android_demo/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':android' 2 | -------------------------------------------------------------------------------- /c/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | libolc.a 3 | example 4 | olc_test 5 | gmon.out 6 | -------------------------------------------------------------------------------- /c/BUILD: -------------------------------------------------------------------------------- 1 | cc_library( 2 | name = "openlocationcode", 3 | srcs = [ 4 | "src/olc.c", 5 | ], 6 | copts = [ 7 | "-std=c99", 8 | "-Wall", 9 | "-Wextra", 10 | "-O2", 11 | ], 12 | hdrs = [ 13 | "src/olc.h", 14 | "src/olc_private.h", 15 | ], 16 | includes = ["src"], 17 | visibility = ["//visibility:private"], 18 | ) 19 | 20 | cc_test( 21 | name = "openlocationcode_test", 22 | size = "small", 23 | srcs = ["openlocationcode_test.cc"], 24 | copts = [ 25 | "-pthread", 26 | "-I@googletest//:include", 27 | ], 28 | linkopts = ["-pthread"], 29 | linkstatic = False, 30 | data = [ 31 | "//test_data", 32 | ], 33 | deps = [ 34 | ":openlocationcode", 35 | "@googletest//:gtest_main", 36 | ], 37 | testonly = True, 38 | visibility = ["//visibility:private"], 39 | ) 40 | 41 | cc_binary( 42 | name = "openlocationcode_example", 43 | srcs = [ 44 | "example.c", 45 | ], 46 | deps = [ 47 | ":openlocationcode", 48 | ], 49 | visibility = ["//visibility:private"], 50 | ) -------------------------------------------------------------------------------- /c/README.md: -------------------------------------------------------------------------------- 1 | # Open Location Code C API 2 | 3 | This is the C implementation of the Open Location Code API. 4 | 5 | # Code Style and Formatting 6 | 7 | Code style is based on Googles formatting rules. Code must be formatted 8 | using `clang-format`. 9 | 10 | The `clang_check.sh` script will check for formatting errors, output them, 11 | and automatically format files. 12 | 13 | # Usage 14 | 15 | See example.cc for how to use the library. To run the example, use: 16 | 17 | ``` 18 | bazel run openlocationcode_example 19 | ``` 20 | 21 | # Development 22 | 23 | The library is built/tested using [Bazel](https://bazel.build). To build the library, use: 24 | 25 | ``` 26 | bazel build openlocationcode 27 | ``` 28 | 29 | To run the tests, use: 30 | 31 | ``` 32 | bazel test --test_output=all openlocationcode_test 33 | ``` 34 | 35 | The tests use the CSV files in the test_data folder. Make sure you copy this folder to the 36 | root of your local workspace. 37 | 38 | 39 | # Authors 40 | 41 | * The authors of the C++ implementation, on which this is based. 42 | * [Gonzalo Diethelm](mailto:gonzalo.diethelm@gmail.com) 43 | -------------------------------------------------------------------------------- /c/benchmark/.gitignore: -------------------------------------------------------------------------------- 1 | bm-c 2 | bm-cpp 3 | -------------------------------------------------------------------------------- /c/benchmark/Makefile: -------------------------------------------------------------------------------- 1 | all: run 2 | 3 | RUNS = 1000000 4 | 5 | C_MAIN = bm-c.c 6 | CC_MAIN = bm-cpp.cc 7 | 8 | LD_FLAGS += -lm 9 | 10 | # C compilation 11 | C_FLAGS += -Wall 12 | C_FLAGS += -Wno-comment 13 | C_FLAGS += -I../../c 14 | # C_FLAGS += -Wextra 15 | # C_FLAGS += -DOLC_CHECK_RESULTS 16 | 17 | C_ALL_FLAGS += -std=c99 18 | # C_ALL_FLAGS += -g 19 | 20 | # C++ compilation 21 | CC_FLAGS += -Wall 22 | CC_FLAGS += -Wno-comment 23 | CC_FLAGS += -I../../cpp 24 | # CC_FLAGS += -Wextra 25 | # CC_FLAGS += -DOLC_CHECK_RESULTS 26 | 27 | CC_ALL_FLAGS += -std=c++11 28 | CC_ALL_FLAGS += -pthread 29 | # CC_ALL_FLAGS += -g 30 | 31 | # everything below here should be taken care of automatically 32 | # no need to edit these lines, except to add new binary targets 33 | 34 | LIB_NAME = olc 35 | 36 | C_OBJ = $(C_MAIN:.c=.o) 37 | C_EXE = $(C_MAIN:.c=) 38 | 39 | CC_OBJ = $(CC_MAIN:.cc=.o) 40 | CC_EXE = $(CC_MAIN:.cc=) 41 | 42 | %.o : %.c 43 | cc -c $(C_ALL_FLAGS) $(C_FLAGS) -o $@ $< 44 | 45 | %.o : %.cc 46 | c++ -c $(CC_ALL_FLAGS) $(CC_FLAGS) -o $@ $< 47 | 48 | $(C_EXE): $(C_OBJ) 49 | cc $(C_ALL_FLAGS) -o $(C_EXE) $(LD_FLAGS) $(C_OBJ) -L../../c -l$(LIB_NAME) 50 | 51 | $(CC_EXE): $(CC_OBJ) 52 | c++ $(CC_ALL_FLAGS) -o $(CC_EXE) $(LD_FLAGS) $(CC_OBJ) -L../../cpp -l$(LIB_NAME) 53 | 54 | run: bm-c bm-cpp 55 | ./$(C_EXE) $(RUNS) 56 | ./$(CC_EXE) $(RUNS) 57 | 58 | clean: 59 | rm -f crash-* slow-unit-* *.dSYM 60 | rm -f $(C_OBJ) $(CC_OBJ) 61 | rm -f $(C_EXE) $(CC_EXE) 62 | -------------------------------------------------------------------------------- /c/benchmark/bm-c.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "olc.h" 3 | 4 | #include "shared.c" 5 | 6 | int main(int argc, char* argv[]) { return run(argc, argv); } 7 | 8 | static void encode(void) { 9 | char code[256]; 10 | int len; 11 | OLC_LatLon location; 12 | 13 | // Encodes latitude and longitude into a Plus+Code. 14 | location.lat = data_pos_lat; 15 | location.lon = data_pos_lon; 16 | len = OLC_EncodeDefault(&location, code, 256); 17 | 18 | ASSERT_STR_EQ(code, "8FVC2222+22"); 19 | ASSERT_INT_EQ(len, 11); 20 | } 21 | 22 | static void encode_len(void) { 23 | char code[256]; 24 | int len; 25 | OLC_LatLon location; 26 | 27 | // Encodes latitude and longitude into a Plus+Code with a preferred length. 28 | location.lat = data_pos_lat; 29 | location.lon = data_pos_lon; 30 | len = OLC_Encode(&location, data_pos_len, code, 256); 31 | 32 | ASSERT_STR_EQ(code, "8FVC2222+22GCCCC"); 33 | ASSERT_INT_EQ(len, 16); 34 | } 35 | 36 | static void decode(void) { 37 | OLC_CodeArea code_area; 38 | 39 | // Decodes a Plus+Code back into coordinates. 40 | OLC_Decode(data_code_16, 0, &code_area); 41 | 42 | ASSERT_FLT_EQ(code_area.lo.lat, 47.000062496); 43 | ASSERT_FLT_EQ(code_area.lo.lon, 8.00006250000001); 44 | ASSERT_FLT_EQ(code_area.hi.lat, 47.000062504); 45 | ASSERT_FLT_EQ(code_area.hi.lon, 8.0000625305176); 46 | ASSERT_INT_EQ(code_area.len, 15); 47 | } 48 | 49 | static void is_valid(void) { 50 | // Checks if a Plus+Code is valid. 51 | int ok = !!OLC_IsValid(data_code_16, 0); 52 | ASSERT_INT_EQ(ok, 1); 53 | } 54 | 55 | static void is_full(void) { 56 | // Checks if a Plus+Code is full. 57 | int ok = !!OLC_IsFull(data_code_16, 0); 58 | ASSERT_INT_EQ(ok, 1); 59 | } 60 | 61 | static void is_short(void) { 62 | // Checks if a Plus+Code is short. 63 | int ok = !!OLC_IsShort(data_code_16, 0); 64 | ASSERT_INT_EQ(ok, 0); 65 | } 66 | 67 | static void shorten(void) { 68 | // Shorten a Plus+Codes if possible by the given reference latitude and 69 | // longitude. 70 | char code[256]; 71 | OLC_LatLon location; 72 | location.lat = data_ref_lat; 73 | location.lon = data_ref_lon; 74 | int len = OLC_Shorten(data_code_12, 0, &location, code, 256); 75 | 76 | ASSERT_STR_EQ(code, "CJ+2VX"); 77 | ASSERT_INT_EQ(len, 6); 78 | } 79 | 80 | static void recover(void) { 81 | char code[256]; 82 | OLC_LatLon location; 83 | location.lat = data_ref_lat; 84 | location.lon = data_ref_lon; 85 | // Extends a Plus+Code by the given reference latitude and longitude. 86 | int len = OLC_RecoverNearest(data_code_6, 0, &location, code, 256); 87 | 88 | ASSERT_STR_EQ(code, "9C3W9QCJ+2VX"); 89 | ASSERT_INT_EQ(len, 12); 90 | } 91 | -------------------------------------------------------------------------------- /c/benchmark/bm-cpp.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include "openlocationcode.h" 3 | 4 | #include "shared.c" 5 | 6 | int main(int argc, char* argv[]) 7 | { 8 | return run(argc, argv); 9 | } 10 | 11 | static void encode(void) 12 | { 13 | // Encodes latitude and longitude into a Plus+Code. 14 | std::string code = openlocationcode::Encode({data_pos_lat, data_pos_lon}); 15 | 16 | ASSERT_STR_EQ(code.c_str(), "8FVC2222+22"); 17 | ASSERT_INT_EQ(code.length(), 11); 18 | } 19 | 20 | static void encode_len(void) 21 | { 22 | // Encodes latitude and longitude into a Plus+Code with a preferred length. 23 | std::string code = openlocationcode::Encode({data_pos_lat, data_pos_lon}, data_pos_len); 24 | 25 | ASSERT_STR_EQ(code.c_str(), "8FVC2222+22GCCCC"); 26 | ASSERT_INT_EQ(code.length(), 16); 27 | } 28 | 29 | static void decode(void) 30 | { 31 | // Decodes a Plus+Code back into coordinates. 32 | openlocationcode::CodeArea code_area = openlocationcode::Decode(data_code_16); 33 | 34 | ASSERT_FLT_EQ(code_area.GetLatitudeLo(), 47.000062496); 35 | ASSERT_FLT_EQ(code_area.GetLongitudeLo(), 8.00006250000001); 36 | ASSERT_FLT_EQ(code_area.GetLatitudeHi(), 47.000062504); 37 | ASSERT_FLT_EQ(code_area.GetLongitudeHi(), 8.0000625305176); 38 | ASSERT_INT_EQ(code_area.GetCodeLength(), 15); 39 | } 40 | 41 | static void is_valid(void) 42 | { 43 | // Checks if a Plus+Code is valid. 44 | int ok = openlocationcode::IsValid(data_code_16); 45 | 46 | ASSERT_INT_EQ(ok, 1); 47 | } 48 | 49 | static void is_full(void) 50 | { 51 | // Checks if a Plus+Code is full. 52 | int ok = openlocationcode::IsFull(data_code_16); 53 | 54 | ASSERT_INT_EQ(ok, 1); 55 | } 56 | 57 | static void is_short(void) 58 | { 59 | // Checks if a Plus+Code is short. 60 | int ok = openlocationcode::IsShort(data_code_16); 61 | 62 | ASSERT_INT_EQ(ok, 0); 63 | } 64 | 65 | static void shorten(void) 66 | { 67 | // Shorten a Plus+Codes if possible by the given reference latitude and 68 | // longitude. 69 | std::string short_code = 70 | openlocationcode::Shorten(data_code_12, {data_ref_lat, data_ref_lon}); 71 | 72 | ASSERT_STR_EQ(short_code.c_str(), "CJ+2VX"); 73 | ASSERT_INT_EQ(short_code.length(), 6); 74 | } 75 | 76 | static void recover(void) 77 | { 78 | // Extends a Plus+Code by the given reference latitude and longitude. 79 | std::string recovered_code = 80 | openlocationcode::RecoverNearest(data_code_6, {data_ref_lat, data_ref_lon}); 81 | 82 | ASSERT_STR_EQ(recovered_code.c_str(), "9C3W9QCJ+2VX"); 83 | ASSERT_INT_EQ(recovered_code.length(), 12); 84 | } 85 | -------------------------------------------------------------------------------- /c/clang_check.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Check the C file and format them with clang-format, unless the script is 3 | # running in a GitHub workflow, in which case we just print an error message. 4 | 5 | CLANG_FORMAT="clang-format-5.0" 6 | if hash $CLANG_FORMAT 2>/dev/null; then 7 | echo "clang-format hashed" 8 | elif hash clang-format 2>/dev/null; then 9 | echo "Cannot find $CLANG_FORMAT, using clang-format" 10 | CLANG_FORMAT="clang-format" 11 | else 12 | echo "Cannot find clang-format" 13 | exit 1 14 | fi 15 | 16 | if [ ! -f ".clang-format" ]; then 17 | echo ".clang-format file not found!" 18 | exit 1 19 | fi 20 | 21 | RETURN=0 22 | : 23 | for FILE in `ls *.[ch] */*.[ch]`; do 24 | DIFF=`diff $FILE <($CLANG_FORMAT $FILE)` 25 | if [ $? -ne 0 ]; then 26 | if [ -z "$GITHUB_WORKFLOW" ]; then 27 | echo "Formatting $FILE" >&2 28 | $CLANG_FORMAT -i $FILE 29 | else 30 | echo -e "\e[31m$FILE has formatting errors:\e[30m" >&2 31 | echo "$DIFF" >&2 32 | fi 33 | RETURN=1 34 | fi 35 | done 36 | exit $RETURN 37 | -------------------------------------------------------------------------------- /c/example.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "src/olc.h" 3 | 4 | int main(int argc, char* argv[]) { 5 | char code[256]; 6 | int len; 7 | OLC_LatLon location; 8 | 9 | // Show current version 10 | printf("=== OLC version [%s] -- %d -- [%d] [%d] [%d] ===\n", OLC_VERSION_STR, 11 | OLC_VERSION_NUM, OLC_VERSION_MAJOR, OLC_VERSION_MINOR, 12 | OLC_VERSION_PATCH); 13 | 14 | // Encodes latitude and longitude into a Plus+Code. 15 | location.lat = 47.0000625; 16 | location.lon = 8.0000625; 17 | len = OLC_EncodeDefault(&location, code, 256); 18 | printf("%s (%d)\n", code, len); 19 | // => "8FVC2222+22" 20 | 21 | // Encodes latitude and longitude into a Plus+Code with a preferred length. 22 | len = OLC_Encode(&location, 16, code, 256); 23 | printf("%s (%d)\n", code, len); 24 | // => "8FVC2222+22GCCCC" 25 | 26 | // Decodes a Plus+Code back into coordinates. 27 | OLC_CodeArea code_area; 28 | OLC_Decode(code, 0, &code_area); 29 | printf("Code length: %.15f : %.15f to %.15f : %.15f (%lu)\n", 30 | code_area.lo.lat, code_area.lo.lon, code_area.hi.lat, code_area.hi.lon, 31 | code_area.len); 32 | // => 47.000062496 8.00006250000001 47.000062504 8.0000625305176 16 33 | 34 | int is_valid = OLC_IsValid(code, 0); 35 | printf("Is Valid: %d\n", is_valid); 36 | // => true 37 | 38 | int is_full = OLC_IsFull(code, 0); 39 | printf("Is Full: %d\n", is_full); 40 | // => true 41 | 42 | int is_short = OLC_IsShort(code, 0); 43 | printf("Is Short: %d\n", is_short); 44 | // => true 45 | 46 | // Shorten a Plus+Codes if possible by the given reference latitude and 47 | // longitude. 48 | const char* orig = "9C3W9QCJ+2VX"; 49 | printf("Original: %s\n", orig); 50 | location.lat = 51.3708675; 51 | location.lon = -1.217765625; 52 | len = OLC_Shorten(orig, 0, &location, code, 256); 53 | printf("Shortened: %s\n", code); 54 | // => "CJ+2VX" 55 | 56 | // Extends a Plus+Code by the given reference latitude and longitude. 57 | OLC_RecoverNearest("CJ+2VX", 0, &location, code, 256); 58 | printf("Recovered: %s\n", code); 59 | // => orig 60 | 61 | return 0; 62 | } 63 | -------------------------------------------------------------------------------- /cpp/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.a 3 | openlocationcode_example 4 | -------------------------------------------------------------------------------- /cpp/BUILD: -------------------------------------------------------------------------------- 1 | # Library to handle Plus Codes 2 | cc_library( 3 | name = "openlocationcode", 4 | srcs = [ 5 | "openlocationcode.cc", 6 | ], 7 | hdrs = [ 8 | "codearea.h", 9 | "openlocationcode.h", 10 | ], 11 | copts = [ 12 | "-pthread", 13 | "-Wall", 14 | "-Wextra", 15 | "-O2", 16 | ], 17 | linkopts = ["-pthread"], 18 | deps = [ 19 | ":codearea", 20 | ], 21 | ) 22 | 23 | # Code area library, used by Open Location Code 24 | cc_library( 25 | name = "codearea", 26 | srcs = [ 27 | "codearea.cc", 28 | ], 29 | hdrs = [ 30 | "codearea.h", 31 | ], 32 | visibility = ["//visibility:private"], # Keep private unless needed elsewhere 33 | ) 34 | 35 | # Unit test for Open Location Code implementations 36 | cc_test( 37 | name = "openlocationcode_test", 38 | size = "small", 39 | srcs = ["openlocationcode_test.cc"], 40 | copts = [ 41 | "-pthread", 42 | "-I@googletest//:include", 43 | ], 44 | linkopts = ["-pthread"], 45 | linkstatic = False, 46 | data = [ 47 | "//test_data", 48 | ], 49 | deps = [ 50 | ":openlocationcode", 51 | "@googletest//:gtest_main", 52 | ], 53 | testonly = True, 54 | ) 55 | 56 | # Example binary for Open Location Code 57 | cc_binary( 58 | name = "openlocationcode_example", 59 | srcs = [ 60 | "openlocationcode_example.cc", 61 | ], 62 | deps = [ 63 | ":openlocationcode", 64 | ], 65 | ) -------------------------------------------------------------------------------- /cpp/README.md: -------------------------------------------------------------------------------- 1 | # Open Location Code C++ API 2 | This is the C++ implementation of the Open Location Code API. 3 | 4 | # Usage 5 | 6 | See openlocationcode_example.cc for how to use the library. To run the example, use: 7 | 8 | ``` 9 | bazel run openlocationcode_example 10 | ``` 11 | 12 | # Development 13 | 14 | The library is built/tested using [Bazel](https://bazel.build). To build the library, use: 15 | 16 | ``` 17 | bazel build openlocationcode 18 | ``` 19 | 20 | To run the tests, use: 21 | 22 | ``` 23 | bazel test --test_output=all openlocationcode_test 24 | ``` 25 | 26 | The tests use the CSV files in the test_data folder. Make sure you copy this folder to the 27 | root of your local workspace. 28 | 29 | # Formatting 30 | 31 | Code must be formatted using `clang-format`, and this will be checked in the 32 | tests. You can format your code using the script: 33 | 34 | ``` 35 | sh clang_check.sh 36 | ``` 37 | -------------------------------------------------------------------------------- /cpp/clang_check.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Check formatting of C++ source files using clang-format. 3 | # If running on TravisCI, will display the lines that need changing, 4 | # otherwise it will format the files in place. 5 | 6 | CLANG_FORMAT="clang-format-5.0" 7 | if hash $CLANG_FORMAT 2>/dev/null; then 8 | echo "clang-format hashed" 9 | elif hash clang-format 2>/dev/null; then 10 | echo "Cannot find $CLANG_FORMAT, using clang-format" 11 | CLANG_FORMAT="clang-format" 12 | else 13 | echo "Cannot find clang-format" 14 | exit 1 15 | fi 16 | $CLANG_FORMAT --version 17 | 18 | if [ ! -f ".clang-format" ]; then 19 | echo ".clang-format file not found!" 20 | exit 1 21 | fi 22 | 23 | RETURN=0 24 | : 25 | for FILE in `ls *.cc *.h`; do 26 | DIFF=`diff $FILE <($CLANG_FORMAT $FILE)` 27 | if [ $? -ne 0 ]; then 28 | if [ -z "$TRAVIS" ]; then 29 | echo "Formatting $FILE" >&2 30 | $CLANG_FORMAT -i $FILE 31 | else 32 | echo -e "\e[31m$FILE has formatting errors:\e[30m" >&2 33 | echo "$DIFF" >&2 34 | fi 35 | RETURN=1 36 | fi 37 | done 38 | exit $RETURN 39 | -------------------------------------------------------------------------------- /cpp/codearea.cc: -------------------------------------------------------------------------------- 1 | #include "codearea.h" 2 | 3 | #include 4 | 5 | namespace openlocationcode { 6 | 7 | const double kLatitudeMaxDegrees = 90; 8 | const double kLongitudeMaxDegrees = 180; 9 | 10 | CodeArea::CodeArea(double latitude_lo, double longitude_lo, double latitude_hi, 11 | double longitude_hi, size_t code_length) { 12 | latitude_lo_ = latitude_lo; 13 | longitude_lo_ = longitude_lo; 14 | latitude_hi_ = latitude_hi; 15 | longitude_hi_ = longitude_hi; 16 | code_length_ = code_length; 17 | } 18 | 19 | double CodeArea::GetLatitudeLo() const { return latitude_lo_; } 20 | 21 | double CodeArea::GetLongitudeLo() const { return longitude_lo_; } 22 | 23 | double CodeArea::GetLatitudeHi() const { return latitude_hi_; } 24 | 25 | double CodeArea::GetLongitudeHi() const { return longitude_hi_; } 26 | 27 | size_t CodeArea::GetCodeLength() const { return code_length_; } 28 | 29 | LatLng CodeArea::GetCenter() const { 30 | const double latitude_center = std::min( 31 | latitude_lo_ + (latitude_hi_ - latitude_lo_) / 2, kLatitudeMaxDegrees); 32 | const double longitude_center = 33 | std::min(longitude_lo_ + (longitude_hi_ - longitude_lo_) / 2, 34 | kLongitudeMaxDegrees); 35 | const LatLng center = {latitude_center, longitude_center}; 36 | return center; 37 | } 38 | 39 | } // namespace openlocationcode 40 | -------------------------------------------------------------------------------- /cpp/codearea.h: -------------------------------------------------------------------------------- 1 | #ifndef LOCATION_OPENLOCATIONCODE_CODEAREA_H_ 2 | #define LOCATION_OPENLOCATIONCODE_CODEAREA_H_ 3 | 4 | #include 5 | 6 | namespace openlocationcode { 7 | 8 | struct LatLng { 9 | double latitude; 10 | double longitude; 11 | }; 12 | 13 | class CodeArea { 14 | public: 15 | CodeArea(double latitude_lo, double longitude_lo, double latitude_hi, 16 | double longitude_hi, size_t code_length); 17 | double GetLatitudeLo() const; 18 | double GetLongitudeLo() const; 19 | double GetLatitudeHi() const; 20 | double GetLongitudeHi() const; 21 | size_t GetCodeLength() const; 22 | LatLng GetCenter() const; 23 | 24 | private: 25 | double latitude_lo_; 26 | double longitude_lo_; 27 | double latitude_hi_; 28 | double longitude_hi_; 29 | size_t code_length_; 30 | }; 31 | 32 | } // namespace openlocationcode 33 | 34 | #endif // LOCATION_OPENLOCATIONCODE_CODEAREA_H_ 35 | -------------------------------------------------------------------------------- /cpp/openlocationcode_example.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "openlocationcode.h" 5 | 6 | int main() { 7 | // Encodes latitude and longitude into a Plus+Code. 8 | std::string code = openlocationcode::Encode({47.0000625, 8.0000625}); 9 | std::cout << "Encoded: " << code << std::endl; 10 | // => "8FVC2222+22" 11 | 12 | // Encodes latitude and longitude into a Plus+Code with a preferred length. 13 | code = openlocationcode::Encode({47.0000625, 8.0000625}, 16); 14 | std::cout << "Encoded 16: " << code << std::endl; 15 | // => "8FVC2222+22GCCCC" 16 | 17 | // Decodes a Plus+Code back into coordinates. 18 | openlocationcode::CodeArea code_area = openlocationcode::Decode(code); 19 | std::cout << "Code length: " << std::fixed << std::setprecision(15) << ' ' 20 | << code_area.GetLatitudeLo() // 47.000062479999997 21 | << ' ' << code_area.GetLongitudeLo() // 8.000062500000013 22 | << ' ' << code_area.GetLatitudeHi() // 47.000062520000000 23 | << ' ' << code_area.GetLongitudeHi() // 8.000062622070317 24 | << ' ' << code_area.GetCodeLength() // 15 25 | << std::endl; 26 | 27 | // Checks if a Plus+Code is valid. 28 | bool isValid = openlocationcode::IsValid(code); 29 | std::cout << "Is valid? " << isValid << std::endl; 30 | // => true 31 | 32 | // Checks if a Plus+Code is full. 33 | bool isFull = openlocationcode::IsFull(code); 34 | std::cout << "Is full? " << isFull << std::endl; 35 | // => true 36 | 37 | // Checks if a Plus+Code is short. 38 | bool isShort = openlocationcode::IsShort(code); 39 | std::cout << "Is short? " << isShort << std::endl; 40 | // => false 41 | 42 | // Shorten a Plus+Codes if possible by the given reference latitude and 43 | // longitude. 44 | std::string short_code = 45 | openlocationcode::Shorten("9C3W9QCJ+2VX", {51.3708675, -1.217765625}); 46 | std::cout << "Shortened: " << short_code << std::endl; 47 | // => "CJ+2VX" 48 | 49 | // Extends a Plus+Code by the given reference latitude and longitude. 50 | std::string recovered_code = 51 | openlocationcode::RecoverNearest("CJ+2VX", {51.3708675, -1.217765625}); 52 | std::cout << "Recovered: " << recovered_code << std::endl; 53 | // => "9C3W9QCJ+2VX" 54 | } 55 | -------------------------------------------------------------------------------- /dart/.gitignore: -------------------------------------------------------------------------------- 1 | .packages 2 | pubspec.lock 3 | .dart_tool -------------------------------------------------------------------------------- /dart/README.md: -------------------------------------------------------------------------------- 1 | # dart library for Open Location Code 2 | 3 | ## Formatting 4 | 5 | Code **must** be formatted using `dart format`. 6 | 7 | To format your files, just run `checks.sh` or: 8 | 9 | ```shell 10 | dart format . 11 | ``` 12 | 13 | The TravisCI test **will fail if any files need formatting**. 14 | 15 | ## Hints 16 | 17 | The TravisCI test uses `dartanalyzer` to check the library for improvements. IF 18 | any are found the TravisCI tests **will fail**. 19 | 20 | ## Testing 21 | 22 | To test the dart version first download the dart sdk from 23 | [Dart main site](http://www.dartlang.org) and run this from the repository root 24 | directory: 25 | 26 | ``` 27 | ~/open-location-code$ cd dart && dart test 28 | ``` 29 | -------------------------------------------------------------------------------- /dart/checks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Check the formatting of the Dart files and perform static analysis of the code 3 | # with dartanalyzer. 4 | # Run from within the dart directory. 5 | 6 | DART_CMD=dart 7 | $DART_CMD --version >/dev/null 2>&1 8 | if [ $? -ne 0 ]; then 9 | DART_CMD=/usr/lib/dart/bin/dart 10 | fi 11 | 12 | # Define the default return code. 13 | RETURN=0 14 | 15 | # For every dart file, check the formatting. 16 | for FILE in `find * | egrep "\.dart$"`; do 17 | FORMATTED=`$DART_CMD format -o none --set-exit-if-changed "$FILE"` 18 | if [ $? -ne 0 ]; then 19 | if [ -z "$TRAVIS" ]; then 20 | # Running locally, we can just format the file. Use colour codes. 21 | echo -e "\e[1;34m" 22 | $DART_CMD format $FILE 23 | echo -e "\e[0m" 24 | else 25 | # On TravisCI, send a comment with the diff to the pull request. 26 | DIFF=`echo "$FORMATTED" | diff $FILE -` 27 | echo -e "\e[1;31mFile has formatting errors: $FILE\e[0m" 28 | echo "$DIFF" 29 | RETURN=1 30 | go run ../travis-utils/github_comments.go --pr "$TRAVIS_PULL_REQUEST" \ 31 | --comment '**File has `dartfmt` errors that must be fixed**. Here is a diff, or run `checks.sh`:'"
$DIFF
" \ 32 | --file "dart/$FILE" \ 33 | --commit "$TRAVIS_PULL_REQUEST_SHA" 34 | fi 35 | fi 36 | ANALYSIS=`$DART_CMD analyze "$FILE"` 37 | echo "$ANALYSIS" | grep "No issues found" >/dev/null 38 | if [ $? -ne 0 ]; then 39 | echo -e "\e[1;31mStatic analysis problems: $FILE\e[0m" 40 | echo "$ANALYSIS" 41 | if [ "$TRAVIS" != "" ]; then 42 | # On TravisCI, send a comment with the diff to the pull request. 43 | RETURN=1 44 | go run ../travis-utils/github_comments.go --pr "$TRAVIS_PULL_REQUEST" \ 45 | --comment '**File has `dartanalyzer` errors that must be addressed**:'"
$ANALYSIS
" \ 46 | --file "dart/$FILE" \ 47 | --commit "$TRAVIS_PULL_REQUEST_SHA" 48 | fi 49 | fi 50 | done 51 | 52 | if [ $RETURN -ne 0 ]; then 53 | echo -e "\e[1;31mFiles have issues that must be addressed\e[0m" 54 | else 55 | echo -e "\e[1;32mFiles pass all checks\e[0m" 56 | fi 57 | exit $RETURN 58 | -------------------------------------------------------------------------------- /dart/lib/open_location_code.dart: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Google Inc. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | library open_location_code; 18 | 19 | export 'src/open_location_code.dart'; 20 | -------------------------------------------------------------------------------- /dart/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: open_location_code 2 | description: Plus Codes are short, generated codes that can be used like street addresses, for places where street addresses don't exist. 3 | version: 0.0.1 4 | homepage: https://maps.google.com/pluscodes/ 5 | environment: 6 | sdk: '^2.19.6' 7 | dev_dependencies: 8 | test: ^1.24.3 9 | -------------------------------------------------------------------------------- /dart/test/benchmark_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:open_location_code/open_location_code.dart' as olc; 2 | import 'package:test/test.dart'; 3 | import 'dart:math'; 4 | 5 | void main() { 6 | test('Benchmarking encode and decode', () { 7 | var now = DateTime.now(); 8 | var random = Random(now.millisecondsSinceEpoch); 9 | var testData = []; 10 | for (var i = 0; i < 1000000; i++) { 11 | var lat = random.nextDouble() * 180 - 90; 12 | var lng = random.nextDouble() * 360 - 180; 13 | var exp = pow(10, (random.nextDouble() * 10).toInt()); 14 | lat = (lat * exp).round() / exp; 15 | lng = (lng * exp).round() / exp; 16 | var length = 2 + (random.nextDouble() * 13).round(); 17 | if (length < 10 && length % 2 == 1) { 18 | length += 1; 19 | } 20 | var code = olc.encode(lat, lng, codeLength: length); 21 | olc.decode(code); 22 | testData.add([lat, lng, length, code]); 23 | } 24 | var stopwatch = Stopwatch()..start(); 25 | for (var i = 0; i < testData.length; i++) { 26 | olc.encode(testData[i][0], testData[i][1], codeLength: testData[i][2]); 27 | } 28 | var duration = stopwatch.elapsedMicroseconds; 29 | print( 30 | 'Encoding benchmark ${testData.length}, duration ${duration} usec, ' 31 | 'average ${duration / testData.length} usec', 32 | ); 33 | 34 | stopwatch = Stopwatch()..start(); 35 | for (var i = 0; i < testData.length; i++) { 36 | olc.decode(testData[i][3]); 37 | } 38 | duration = stopwatch.elapsedMicroseconds; 39 | print( 40 | 'Decoding benchmark ${testData.length}, duration ${duration} usec, ' 41 | 'average ${duration / testData.length} usec', 42 | ); 43 | }); 44 | } 45 | -------------------------------------------------------------------------------- /dart/test/clip_latitude_test.dart: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Google Inc. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import 'package:open_location_code/open_location_code.dart' as olc; 18 | import 'package:test/test.dart'; 19 | 20 | void main() { 21 | test('Clip latitude test', () { 22 | expect(olc.clipLatitude(100.0), 90.0); 23 | expect(olc.clipLatitude(-100.0), -90.0); 24 | expect(olc.clipLatitude(10.0), 10.0); 25 | expect(olc.clipLatitude(-10.0), -10.0); 26 | }); 27 | } 28 | -------------------------------------------------------------------------------- /dart/test/compute_precision_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:open_location_code/open_location_code.dart' as olc; 2 | import 'package:test/test.dart'; 3 | 4 | void main() { 5 | test('Compute precision test', () { 6 | expect(olc.computeLatitudePrecision(10), 0.000125); 7 | expect(olc.computeLatitudePrecision(11), 0.000025); 8 | }); 9 | } 10 | -------------------------------------------------------------------------------- /dart/test/decode_test.dart: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Google Inc. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import 'package:open_location_code/open_location_code.dart' as olc; 18 | import 'package:test/test.dart'; 19 | import 'utils.dart'; 20 | 21 | // code,lat,lng,latLo,lngLo,latHi,lngHi 22 | void checkEncodeDecode(String csvLine) { 23 | var elements = csvLine.split(','); 24 | var code = elements[0]; 25 | num len = int.parse(elements[1]); 26 | num latLo = double.parse(elements[2]); 27 | num lngLo = double.parse(elements[3]); 28 | num latHi = double.parse(elements[4]); 29 | num lngHi = double.parse(elements[5]); 30 | var codeArea = olc.decode(code); 31 | expect(codeArea.codeLength, equals(len)); 32 | expect(codeArea.south, closeTo(latLo, 0.001)); 33 | expect(codeArea.north, closeTo(latHi, 0.001)); 34 | expect(codeArea.west, closeTo(lngLo, 0.001)); 35 | expect(codeArea.east, closeTo(lngHi, 0.001)); 36 | } 37 | 38 | void main() { 39 | test('Check decode', () { 40 | csvLinesFromFile('decoding.csv').forEach(checkEncodeDecode); 41 | }); 42 | 43 | test('MaxCodeLength', () { 44 | // Check that we do not return a code longer than is valid. 45 | var code = olc.encode(51.3701125, -10.202665625, codeLength: 1000000); 46 | expect(code.length, 16); 47 | expect(olc.isValid(code), true); 48 | 49 | // Extend the code with a valid character and make sure it is still valid. 50 | var tooLongCode = code + 'W'; 51 | expect(olc.isValid(tooLongCode), true); 52 | 53 | // Extend the code with an invalid character and make sure it is invalid. 54 | tooLongCode = code + 'U'; 55 | expect(olc.isValid(tooLongCode), false); 56 | }); 57 | } 58 | -------------------------------------------------------------------------------- /dart/test/short_code_test.dart: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Google Inc. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import 'package:open_location_code/open_location_code.dart' as olc; 18 | import 'package:test/test.dart'; 19 | import 'utils.dart'; 20 | 21 | // full code,lat,lng,shortcode 22 | void checkShortCode(String csvLine) { 23 | var elements = csvLine.split(','); 24 | var code = elements[0]; 25 | num lat = double.parse(elements[1]); 26 | num lng = double.parse(elements[2]); 27 | var shortCode = elements[3]; 28 | var testType = elements[4]; 29 | if (testType == 'B' || testType == 'S') { 30 | var short = olc.shorten(code, lat, lng); 31 | expect(short, equals(shortCode)); 32 | } 33 | if (testType == 'B' || testType == 'R') { 34 | var expanded = olc.recoverNearest(shortCode, lat, lng); 35 | expect(expanded, equals(code)); 36 | } 37 | } 38 | 39 | void main() { 40 | test('Check short codes', () { 41 | csvLinesFromFile('shortCodeTests.csv').forEach(checkShortCode); 42 | }); 43 | } 44 | -------------------------------------------------------------------------------- /dart/test/utils.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | import 'package:path/path.dart' as path; 3 | 4 | List getCsvLines(String fileName) { 5 | return File(fileName) 6 | .readAsLinesSync() 7 | .where((x) => x.isNotEmpty && !x.startsWith('#')) 8 | .map((x) => x.trim()) 9 | .toList(); 10 | } 11 | 12 | // Requires test csv files in a test_data directory under Open Location Code project root. 13 | String testDataPath() { 14 | var projectRoot = Directory.current.parent; 15 | 16 | return path.absolute(projectRoot.path, 'test_data'); 17 | } 18 | 19 | String cvsWithAbsolutePath(String file) => path.absolute(testDataPath(), file); 20 | 21 | List csvLinesFromFile(String file) => 22 | getCsvLines(cvsWithAbsolutePath(file)); 23 | -------------------------------------------------------------------------------- /dart/test/validity_test.dart: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Google Inc. All rights reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import 'package:open_location_code/open_location_code.dart' as olc; 18 | import 'package:test/test.dart'; 19 | import 'utils.dart'; 20 | 21 | // code,isValid,isShort,isFull 22 | void checkValidity(String csvLine) { 23 | var elements = csvLine.split(','); 24 | var code = elements[0]; 25 | var isValid = elements[1] == 'true'; 26 | var isShort = elements[2] == 'true'; 27 | var isFull = elements[3] == 'true'; 28 | expect(olc.isValid(code), equals(isValid)); 29 | expect(olc.isShort(code), equals(isShort)); 30 | expect(olc.isFull(code), equals(isFull)); 31 | } 32 | 33 | void main() { 34 | test('Check Validity', () { 35 | csvLinesFromFile('validityTests.csv').forEach(checkValidity); 36 | }); 37 | } 38 | -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | PlusCodeDatafield 4 | 5 | 6 | 7 | 8 | 9 | connectiq.builder 10 | 11 | 12 | 13 | 14 | 15 | connectiq.projectNature 16 | 17 | 18 | -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/monkey.jungle: -------------------------------------------------------------------------------- 1 | project.manifest = manifest.xml 2 | -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/resources-d2bravo/drawables.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/resources-d2bravo/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/open-location-code/3cf9f806af4d1c47388badea615064544cec94f5/garmin/PlusCodeDatafield/resources-d2bravo/launcher_icon.png -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/resources-d2bravo_titanium/drawables.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/resources-d2bravo_titanium/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/open-location-code/3cf9f806af4d1c47388badea615064544cec94f5/garmin/PlusCodeDatafield/resources-d2bravo_titanium/launcher_icon.png -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/resources-edge1030/drawables.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/resources-edge1030/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/open-location-code/3cf9f806af4d1c47388badea615064544cec94f5/garmin/PlusCodeDatafield/resources-edge1030/launcher_icon.png -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/resources-edge1030bontrager/drawables.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/resources-edge1030bontrager/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/open-location-code/3cf9f806af4d1c47388badea615064544cec94f5/garmin/PlusCodeDatafield/resources-edge1030bontrager/launcher_icon.png -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/resources-edge130/drawables.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/resources-edge130/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/open-location-code/3cf9f806af4d1c47388badea615064544cec94f5/garmin/PlusCodeDatafield/resources-edge130/launcher_icon.png -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/resources-edge520plus/drawables.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/resources-edge520plus/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/open-location-code/3cf9f806af4d1c47388badea615064544cec94f5/garmin/PlusCodeDatafield/resources-edge520plus/launcher_icon.png -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/resources-edge820/drawables.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/resources-edge820/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/open-location-code/3cf9f806af4d1c47388badea615064544cec94f5/garmin/PlusCodeDatafield/resources-edge820/launcher_icon.png -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/resources-edge_1000/drawables.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/resources-edge_1000/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/open-location-code/3cf9f806af4d1c47388badea615064544cec94f5/garmin/PlusCodeDatafield/resources-edge_1000/launcher_icon.png -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/resources-edge_520/drawables.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/resources-edge_520/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/open-location-code/3cf9f806af4d1c47388badea615064544cec94f5/garmin/PlusCodeDatafield/resources-edge_520/launcher_icon.png -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/resources-epix/drawables.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/resources-epix/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/open-location-code/3cf9f806af4d1c47388badea615064544cec94f5/garmin/PlusCodeDatafield/resources-epix/launcher_icon.png -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/resources-fenix3/drawables.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/resources-fenix3/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/open-location-code/3cf9f806af4d1c47388badea615064544cec94f5/garmin/PlusCodeDatafield/resources-fenix3/launcher_icon.png -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/resources-fenix3_hr/drawables.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/resources-fenix3_hr/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/open-location-code/3cf9f806af4d1c47388badea615064544cec94f5/garmin/PlusCodeDatafield/resources-fenix3_hr/launcher_icon.png -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/resources-fenix5/drawables.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/resources-fenix5/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/open-location-code/3cf9f806af4d1c47388badea615064544cec94f5/garmin/PlusCodeDatafield/resources-fenix5/launcher_icon.png -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/resources-fenix5plus/drawables.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/resources-fenix5plus/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/open-location-code/3cf9f806af4d1c47388badea615064544cec94f5/garmin/PlusCodeDatafield/resources-fenix5plus/launcher_icon.png -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/resources-fenix5s/drawables.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/resources-fenix5s/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/open-location-code/3cf9f806af4d1c47388badea615064544cec94f5/garmin/PlusCodeDatafield/resources-fenix5s/launcher_icon.png -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/resources-fenix5x/drawables.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/resources-fenix5x/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/open-location-code/3cf9f806af4d1c47388badea615064544cec94f5/garmin/PlusCodeDatafield/resources-fenix5x/launcher_icon.png -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/resources-fenixchronos/drawables.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/resources-fenixchronos/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/open-location-code/3cf9f806af4d1c47388badea615064544cec94f5/garmin/PlusCodeDatafield/resources-fenixchronos/launcher_icon.png -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/resources-fr235/drawables.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/resources-fr235/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/open-location-code/3cf9f806af4d1c47388badea615064544cec94f5/garmin/PlusCodeDatafield/resources-fr235/launcher_icon.png -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/resources-fr630/drawables.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/resources-fr630/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/open-location-code/3cf9f806af4d1c47388badea615064544cec94f5/garmin/PlusCodeDatafield/resources-fr630/launcher_icon.png -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/resources-fr645/drawables.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/resources-fr645/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/open-location-code/3cf9f806af4d1c47388badea615064544cec94f5/garmin/PlusCodeDatafield/resources-fr645/launcher_icon.png -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/resources-fr645m/drawables.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/resources-fr645m/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/open-location-code/3cf9f806af4d1c47388badea615064544cec94f5/garmin/PlusCodeDatafield/resources-fr645m/launcher_icon.png -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/resources-fr735xt/drawables.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/resources-fr735xt/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/open-location-code/3cf9f806af4d1c47388badea615064544cec94f5/garmin/PlusCodeDatafield/resources-fr735xt/launcher_icon.png -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/resources-fr920xt/drawables.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/resources-fr920xt/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/open-location-code/3cf9f806af4d1c47388badea615064544cec94f5/garmin/PlusCodeDatafield/resources-fr920xt/launcher_icon.png -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/resources-fr935/drawables.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/resources-fr935/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/open-location-code/3cf9f806af4d1c47388badea615064544cec94f5/garmin/PlusCodeDatafield/resources-fr935/launcher_icon.png -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/resources-oregon7xx/drawables.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/resources-oregon7xx/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/open-location-code/3cf9f806af4d1c47388badea615064544cec94f5/garmin/PlusCodeDatafield/resources-oregon7xx/launcher_icon.png -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/resources-rino7xx/drawables.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/resources-rino7xx/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/open-location-code/3cf9f806af4d1c47388badea615064544cec94f5/garmin/PlusCodeDatafield/resources-rino7xx/launcher_icon.png -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/resources-vivoactive/drawables.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/resources-vivoactive/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/open-location-code/3cf9f806af4d1c47388badea615064544cec94f5/garmin/PlusCodeDatafield/resources-vivoactive/launcher_icon.png -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/resources-vivoactive3/drawables.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/resources-vivoactive3/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/open-location-code/3cf9f806af4d1c47388badea615064544cec94f5/garmin/PlusCodeDatafield/resources-vivoactive3/launcher_icon.png -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/resources-vivoactive3m/drawables.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/resources-vivoactive3m/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/open-location-code/3cf9f806af4d1c47388badea615064544cec94f5/garmin/PlusCodeDatafield/resources-vivoactive3m/launcher_icon.png -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/resources-vivoactive_hr/drawables.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/resources-vivoactive_hr/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/open-location-code/3cf9f806af4d1c47388badea615064544cec94f5/garmin/PlusCodeDatafield/resources-vivoactive_hr/launcher_icon.png -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/resources-vivoactive_hr/resources-fenix3_hr/drawables.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/resources-vivoactive_hr/resources-fenix3_hr/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/open-location-code/3cf9f806af4d1c47388badea615064544cec94f5/garmin/PlusCodeDatafield/resources-vivoactive_hr/resources-fenix3_hr/launcher_icon.png -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/resources/drawables.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/resources/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/open-location-code/3cf9f806af4d1c47388badea615064544cec94f5/garmin/PlusCodeDatafield/resources/launcher_icon.png -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/resources/layouts.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | 10 | 11 | 12 | 15 | 16 | 17 | 20 | 21 | 22 | 25 | 26 | 27 | 30 | 31 | -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/resources/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Plus Codes 3 | plus.codes 4 | --- 5 | 6 | -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/source/PlusCodeBackground.mc: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Google Inc. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the 'License'); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an 'AS IS' BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | using Toybox.WatchUi as Ui; 16 | using Toybox.Application as App; 17 | using Toybox.Graphics as Gfx; 18 | 19 | /** 20 | * Provides a Drawable object for the background of the datafield. 21 | * It's used in the layout. 22 | */ 23 | class PlusCodeBackground extends Ui.Drawable { 24 | 25 | hidden var mColor; 26 | 27 | function initialize() { 28 | var dictionary = { 29 | :identifier => "Background" 30 | }; 31 | 32 | Drawable.initialize(dictionary); 33 | } 34 | 35 | function setColor(color) { 36 | mColor = color; 37 | } 38 | 39 | function draw(dc) { 40 | dc.setColor(Gfx.COLOR_TRANSPARENT, mColor); 41 | dc.clear(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /garmin/PlusCodeDatafield/source/PlusCodeDatafield.mc: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Google Inc. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the 'License'); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an 'AS IS' BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | using Toybox.Application as App; 16 | 17 | /** 18 | * Main class to draw the datafield. 19 | * Initialises and returns the app. 20 | */ 21 | class PlusCodeDatafield extends App.AppBase { 22 | 23 | function initialize() { 24 | AppBase.initialize(); 25 | } 26 | 27 | // onStart() is called on application start up 28 | function onStart(state) { 29 | } 30 | 31 | // onStop() is called when your application is exiting 32 | function onStop(state) { 33 | } 34 | 35 | // Return the initial view of your application here 36 | function getInitialView() { 37 | return [ new PlusCodeView() ]; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /go/.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | olc-fuzz.zip 3 | -------------------------------------------------------------------------------- /go/README.md: -------------------------------------------------------------------------------- 1 | [![GoDoc](https://godoc.org/github.com/google/open-location-code/go?status.svg)](http://godoc.org/github.com/google/open-location-code/go) 2 | 3 | # Formatting 4 | 5 | Go files must be formatted with [gofmt](https://golang.org/cmd/gofmt/), and the 6 | tests will check that this is the case. If the files are not correctly 7 | formatted, the tests will fail. 8 | 9 | You can format your files by running: 10 | 11 | gofmt -w -s . 12 | 13 | # Testing 14 | 15 | Run the unit tests from within the `go` directory with: 16 | 17 | ``` 18 | go test . -v 19 | ``` 20 | 21 | To also run the benchmark tests, run: 22 | 23 | ``` 24 | go test -bench=. . -v 25 | ``` 26 | 27 | ## Test with Go-Fuzz 28 | 29 | go get github.com/dvyukov/go-fuzz/... 30 | 31 | go generate github.com/google/open-location-code/go 32 | 33 | go-fuzz-build github.com/google/open-location-code/go 34 | go-fuzz -bin=./olc-fuzz.zip -workdir=/tmp/olc-fuzz 35 | 36 | # Install 37 | 38 | go get github.com/google/open-location-code/go 39 | 40 | -------------------------------------------------------------------------------- /go/corpus/.gitignore: -------------------------------------------------------------------------------- 1 | *.txt 2 | [0-9a-f][0-9a-f][0-9a-f][0-9a-f]* 3 | -------------------------------------------------------------------------------- /go/corpus/gen.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Tamás Gulácsi. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the 'License'); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an 'AS IS' BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // This little program generates 00%d.code.txt corpus for go-fuzz-build, 16 | // into the given directory. 17 | package main 18 | 19 | import ( 20 | "bytes" 21 | "flag" 22 | "fmt" 23 | "io/ioutil" 24 | "log" 25 | "os" 26 | "path/filepath" 27 | "strings" 28 | ) 29 | 30 | func main() { 31 | flagDir := flag.String("dest", ".", "destination directory") 32 | flagTestData := flag.String("test-data", filepath.Join("..", "..", "test_data"), "the js test_data with the csv files for tests") 33 | flag.Parse() 34 | 35 | if err := extractCorpus(*flagDir, *flagTestData); err != nil { 36 | log.Fatal(err) 37 | } 38 | } 39 | 40 | func extractCorpus(dir, src string) error { 41 | fis, err := ioutil.ReadDir(src) 42 | if err != nil { 43 | log.Printf("read test_data from %s: %v", src, err) 44 | return err 45 | } 46 | _ = os.MkdirAll(dir, 0755) 47 | n := 0 48 | for _, fi := range fis { 49 | if !strings.HasSuffix(fi.Name(), ".csv") { 50 | continue 51 | } 52 | fn := filepath.Join(src, fi.Name()) 53 | data, err := ioutil.ReadFile(fn) 54 | if err != nil { 55 | log.Printf("read csv %s: %v", fn, err) 56 | continue 57 | } 58 | for _, row := range bytes.Split(data, []byte{'\n'}) { 59 | if i := bytes.IndexByte(row, '#'); i >= 0 { 60 | row = row[:i] 61 | } 62 | // assume that the first field is the code 63 | if i := bytes.IndexByte(row, ','); i >= 0 { 64 | fn := filepath.Join(dir, fmt.Sprintf("%003d.code.txt", n)) 65 | if err := ioutil.WriteFile(fn, row[:i], 0644); err != nil { 66 | log.Printf("Write %s: %v", fn, err) 67 | continue 68 | } 69 | n++ 70 | } 71 | } 72 | } 73 | return nil 74 | } 75 | -------------------------------------------------------------------------------- /go/decode.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Tamás Gulácsi. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the 'License'); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an 'AS IS' BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package olc 16 | 17 | import ( 18 | "errors" 19 | "strings" 20 | ) 21 | 22 | // Decode decodes an Open Location Code into the location coordinates. 23 | // Returns a CodeArea object that includes the coordinates of the bounding 24 | // box - the lower left, center and upper right. 25 | // 26 | // Longer codes are allowed, but only the first 15 is decoded. 27 | func Decode(code string) (CodeArea, error) { 28 | var area CodeArea 29 | if err := CheckFull(code); err != nil { 30 | return area, err 31 | } 32 | // Strip out separator character, padding characters and convert to upper 33 | // case. 34 | code = StripCode(code) 35 | codeLen := len(code) 36 | if codeLen < 2 { 37 | return area, errors.New("code too short") 38 | } 39 | // lat and lng build up the integer values. 40 | var lat int64 41 | var lng int64 42 | // height and width build up integer values for the height and width of the 43 | // code area. They get set to 1 for the last digit and then multiplied by 44 | // each remaining place. 45 | var height int64 = 1 46 | var width int64 = 1 47 | // Decode the paired digits. 48 | for i := 0; i < pairCodeLen; i += 2 { 49 | lat *= encBase 50 | lng *= encBase 51 | height *= encBase 52 | if i < codeLen { 53 | lat += int64(strings.IndexByte(Alphabet, code[i])) 54 | lng += int64(strings.IndexByte(Alphabet, code[i+1])) 55 | height = 1 56 | } 57 | } 58 | // The paired section has the same resolution for height and width. 59 | width = height 60 | // Decode the grid section. 61 | for i := pairCodeLen; i < maxCodeLen; i++ { 62 | lat *= gridRows 63 | height *= gridRows 64 | lng *= gridCols 65 | width *= gridCols 66 | if i < codeLen { 67 | dval := int64(strings.IndexByte(Alphabet, code[i])) 68 | lat += dval / gridCols 69 | lng += dval % gridCols 70 | height = 1 71 | width = 1 72 | } 73 | } 74 | // Convert everything into degrees and return the code area. 75 | var latDegrees float64 = float64(lat-latMax*finalLatPrecision) / float64(finalLatPrecision) 76 | var lngDegrees float64 = float64(lng-lngMax*finalLngPrecision) / float64(finalLngPrecision) 77 | var heightDegrees float64 = float64(height) / float64(finalLatPrecision) 78 | var widthDegrees float64 = float64(width) / float64(finalLngPrecision) 79 | return CodeArea{ 80 | LatLo: latDegrees, 81 | LngLo: lngDegrees, 82 | LatHi: latDegrees + heightDegrees, 83 | LngHi: lngDegrees + widthDegrees, 84 | Len: codeLen, 85 | }, nil 86 | } 87 | -------------------------------------------------------------------------------- /go/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/google/open-location-code/go 2 | 3 | go 1.12 4 | -------------------------------------------------------------------------------- /go/go.sum: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/open-location-code/3cf9f806af4d1c47388badea615064544cec94f5/go/go.sum -------------------------------------------------------------------------------- /go/olc_gofuzz.go: -------------------------------------------------------------------------------- 1 | //go:build gofuzz 2 | // +build gofuzz 3 | 4 | // Copyright 2015 Tamás Gulácsi. All rights reserved. 5 | // 6 | // Licensed under the Apache License, Version 2.0 (the 'License'); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an 'AS IS' BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | 18 | package olc 19 | 20 | //go:generate go run corpus/gen.go -test-data=../test_data -dest=corpus 21 | 22 | // Fuzz usage: 23 | // 24 | // go get github.com/dvyukov/go-fuzz/... 25 | // 26 | // go-fuzz-build github.com/google/open-location-code/go && go-fuzz -bin=./olc-fuzz.zip -workdir=/tmp/olc-fuzz 27 | func Fuzz(data []byte) int { 28 | code := string(data) 29 | if err := Check(code); err != nil { 30 | return 0 31 | } 32 | area, err := Decode(code) 33 | if err != nil { 34 | return 2 35 | } 36 | if _, err = Decode(Encode(area.LatLo, area.LngLo, len(code))); err != nil { 37 | return 2 38 | } 39 | if _, err = Decode(Encode(area.LatHi, area.LngHi, len(code))); err != nil { 40 | return 2 41 | } 42 | 43 | return 1 44 | } 45 | -------------------------------------------------------------------------------- /java/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore class and jar files 2 | *.class 3 | *.jar 4 | # Ignore generated Maven files 5 | target/* 6 | -------------------------------------------------------------------------------- /java/src/test/java/com/google/openlocationcode/BenchmarkTest.java: -------------------------------------------------------------------------------- 1 | package com.google.openlocationcode; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.Random; 6 | 7 | import org.junit.Before; 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | import org.junit.runners.JUnit4; 11 | 12 | /** Benchmark the encode and decode methods. */ 13 | @RunWith(JUnit4.class) 14 | public class BenchmarkTest { 15 | 16 | public static final int LOOPS = 1000000; 17 | 18 | public static Random generator = new Random(); 19 | 20 | private static class TestData { 21 | 22 | private final double latitude; 23 | private final double longitude; 24 | private final int length; 25 | private final String code; 26 | 27 | public TestData() { 28 | this.latitude = generator.nextDouble() * 180 - 90; 29 | this.longitude = generator.nextDouble() * 360 - 180; 30 | int length = generator.nextInt(11) + 4; 31 | if (length < 10 && length % 2 == 1) { 32 | length += 1; 33 | } 34 | this.length = length; 35 | this.code = OpenLocationCode.encode(this.latitude, this.longitude, this.length); 36 | } 37 | } 38 | 39 | private final List testDataList = new ArrayList<>(); 40 | 41 | @Before 42 | public void setUp() throws Exception { 43 | testDataList.clear(); 44 | for (int i = 0; i < LOOPS; i++) { 45 | testDataList.add(new TestData()); 46 | } 47 | } 48 | 49 | @Test 50 | public void benchmarkEncode() { 51 | long start = System.nanoTime(); 52 | for (TestData testData : testDataList) { 53 | OpenLocationCode.encode(testData.latitude, testData.longitude, testData.length); 54 | } 55 | long microsecs = (System.nanoTime() - start) / 1000; 56 | 57 | System.out.printf( 58 | "Encode %d loops in %d usecs, %.3f usec per call\n", 59 | LOOPS, microsecs, (double) microsecs / LOOPS); 60 | } 61 | 62 | @Test 63 | public void benchmarkDecode() { 64 | long start = System.nanoTime(); 65 | for (TestData testData : testDataList) { 66 | OpenLocationCode.decode(testData.code); 67 | } 68 | long microsecs = (System.nanoTime() - start) / 1000; 69 | 70 | System.out.printf( 71 | "Decode %d loops in %d usecs, %.3f usec per call\n", 72 | LOOPS, microsecs, (double) microsecs / LOOPS); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /java/src/test/java/com/google/openlocationcode/PrecisionTest.java: -------------------------------------------------------------------------------- 1 | package com.google.openlocationcode; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | import org.junit.runner.RunWith; 6 | import org.junit.runners.JUnit4; 7 | 8 | /** Tests size of rectangles defined by Plus Codes of various size. */ 9 | @RunWith(JUnit4.class) 10 | public class PrecisionTest { 11 | 12 | private static final double EPSILON = 1e-10; 13 | 14 | @Test 15 | public void testWidthInDegrees() { 16 | Assert.assertEquals( 17 | new OpenLocationCode("67000000+").decode().getLongitudeWidth(), 20., EPSILON); 18 | Assert.assertEquals( 19 | new OpenLocationCode("67890000+").decode().getLongitudeWidth(), 1., EPSILON); 20 | Assert.assertEquals( 21 | new OpenLocationCode("6789CF00+").decode().getLongitudeWidth(), 0.05, EPSILON); 22 | Assert.assertEquals( 23 | new OpenLocationCode("6789CFGH+").decode().getLongitudeWidth(), 0.0025, EPSILON); 24 | Assert.assertEquals( 25 | new OpenLocationCode("6789CFGH+JM").decode().getLongitudeWidth(), 0.000125, EPSILON); 26 | Assert.assertEquals( 27 | new OpenLocationCode("6789CFGH+JMP").decode().getLongitudeWidth(), 0.00003125, EPSILON); 28 | } 29 | 30 | @Test 31 | public void testHeightInDegrees() { 32 | Assert.assertEquals( 33 | new OpenLocationCode("67000000+").decode().getLatitudeHeight(), 20., EPSILON); 34 | Assert.assertEquals( 35 | new OpenLocationCode("67890000+").decode().getLatitudeHeight(), 1., EPSILON); 36 | Assert.assertEquals( 37 | new OpenLocationCode("6789CF00+").decode().getLatitudeHeight(), 0.05, EPSILON); 38 | Assert.assertEquals( 39 | new OpenLocationCode("6789CFGH+").decode().getLatitudeHeight(), 0.0025, EPSILON); 40 | Assert.assertEquals( 41 | new OpenLocationCode("6789CFGH+JM").decode().getLatitudeHeight(), 0.000125, EPSILON); 42 | Assert.assertEquals( 43 | new OpenLocationCode("6789CFGH+JMP").decode().getLatitudeHeight(), 0.000025, EPSILON); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /java/src/test/java/com/google/openlocationcode/RecoverTest.java: -------------------------------------------------------------------------------- 1 | package com.google.openlocationcode; 2 | 3 | import junit.framework.Assert; 4 | 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | import org.junit.runners.JUnit4; 8 | 9 | /** Test recovery near the poles. */ 10 | @RunWith(JUnit4.class) 11 | public class RecoverTest { 12 | 13 | @Test 14 | public void testRecoveryNearSouthPole() { 15 | OpenLocationCode olc = new OpenLocationCode("XXXXXX+XX"); 16 | Assert.assertEquals("2CXXXXXX+XX", olc.recover(-81.0, 0.0).getCode()); 17 | } 18 | 19 | @Test 20 | public void testRecoveryNearNorthPole() { 21 | OpenLocationCode olc = new OpenLocationCode("2222+22"); 22 | Assert.assertEquals("CFX22222+22", olc.recover(89.6, 0.0).getCode()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /java/src/test/java/com/google/openlocationcode/ShorteningTest.java: -------------------------------------------------------------------------------- 1 | package com.google.openlocationcode; 2 | 3 | import static java.nio.charset.StandardCharsets.UTF_8; 4 | 5 | import java.io.BufferedReader; 6 | import java.io.FileInputStream; 7 | import java.io.InputStream; 8 | import java.io.InputStreamReader; 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | import org.junit.Assert; 13 | import org.junit.Before; 14 | import org.junit.Test; 15 | import org.junit.runner.RunWith; 16 | import org.junit.runners.JUnit4; 17 | 18 | /** Tests shortening functionality of Open Location Code. */ 19 | @RunWith(JUnit4.class) 20 | public class ShorteningTest { 21 | 22 | private static class TestData { 23 | 24 | private final String code; 25 | private final double referenceLatitude; 26 | private final double referenceLongitude; 27 | private final String shortCode; 28 | private final String testType; 29 | 30 | public TestData(String line) { 31 | String[] parts = line.split(","); 32 | if (parts.length != 5) { 33 | throw new IllegalArgumentException("Wrong format of testing data."); 34 | } 35 | this.code = parts[0]; 36 | this.referenceLatitude = Double.parseDouble(parts[1]); 37 | this.referenceLongitude = Double.parseDouble(parts[2]); 38 | this.shortCode = parts[3]; 39 | this.testType = parts[4]; 40 | } 41 | } 42 | 43 | private final List testDataList = new ArrayList<>(); 44 | 45 | @Before 46 | public void setUp() throws Exception { 47 | InputStream testDataStream = new FileInputStream(TestUtils.getTestFile("shortCodeTests.csv")); 48 | BufferedReader reader = new BufferedReader(new InputStreamReader(testDataStream, UTF_8)); 49 | String line; 50 | while ((line = reader.readLine()) != null) { 51 | if (line.startsWith("#")) { 52 | continue; 53 | } 54 | testDataList.add(new TestData(line)); 55 | } 56 | } 57 | 58 | @Test 59 | public void testShortening() { 60 | for (TestData testData : testDataList) { 61 | if (!"B".equals(testData.testType) && !"S".equals(testData.testType)) { 62 | continue; 63 | } 64 | OpenLocationCode olc = new OpenLocationCode(testData.code); 65 | OpenLocationCode shortened = 66 | olc.shorten(testData.referenceLatitude, testData.referenceLongitude); 67 | Assert.assertEquals( 68 | "Wrong shortening of code " + testData.code, testData.shortCode, shortened.getCode()); 69 | } 70 | } 71 | 72 | @Test 73 | public void testRecovering() { 74 | for (TestData testData : testDataList) { 75 | if (!"B".equals(testData.testType) && !"R".equals(testData.testType)) { 76 | continue; 77 | } 78 | OpenLocationCode olc = new OpenLocationCode(testData.shortCode); 79 | OpenLocationCode recovered = 80 | olc.recover(testData.referenceLatitude, testData.referenceLongitude); 81 | Assert.assertEquals(testData.code, recovered.getCode()); 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /java/src/test/java/com/google/openlocationcode/TestUtils.java: -------------------------------------------------------------------------------- 1 | package com.google.openlocationcode; 2 | 3 | import java.io.File; 4 | 5 | public class TestUtils { 6 | // Gets the test file, factoring in whether it's being built from Maven or Bazel. 7 | public static File getTestFile(String testFile) { 8 | String testPath; 9 | String bazelRootPath = System.getenv("JAVA_RUNFILES"); 10 | if (bazelRootPath == null) { 11 | File userDir = new File(System.getProperty("user.dir")); 12 | testPath = userDir.getParent() + "/test_data"; 13 | } else { 14 | testPath = bazelRootPath + "/_main/test_data"; 15 | } 16 | return new File(testPath, testFile); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /java/src/test/java/com/google/openlocationcode/UtilsTest.java: -------------------------------------------------------------------------------- 1 | package com.google.openlocationcode; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | import org.junit.runner.RunWith; 6 | import org.junit.runners.JUnit4; 7 | 8 | /** Tests various util methods. */ 9 | @RunWith(JUnit4.class) 10 | public class UtilsTest { 11 | 12 | public static final double PRECISION = 1e-10; 13 | 14 | @Test 15 | public void testClipping() { 16 | Assert.assertEquals( 17 | "Clipping of negative latitude doesn't work.", 18 | OpenLocationCode.encode(-90, 5), 19 | OpenLocationCode.encode(-91, 5)); 20 | Assert.assertEquals( 21 | "Clipping of positive latitude doesn't work.", 22 | OpenLocationCode.encode(90, 5), 23 | OpenLocationCode.encode(91, 5)); 24 | Assert.assertEquals( 25 | "Clipping of negative longitude doesn't work.", 26 | OpenLocationCode.encode(5, 175), 27 | OpenLocationCode.encode(5, -185)); 28 | Assert.assertEquals( 29 | "Clipping of very long negative longitude doesn't work.", 30 | OpenLocationCode.encode(5, 175), 31 | OpenLocationCode.encode(5, -905)); 32 | Assert.assertEquals( 33 | "Clipping of very long positive longitude doesn't work.", 34 | OpenLocationCode.encode(5, -175), 35 | OpenLocationCode.encode(5, 905)); 36 | } 37 | 38 | @Test 39 | public void testMaxCodeLength() { 40 | // Check that we do not return a code longer than is valid. 41 | String code = OpenLocationCode.encode(51.3701125, -10.202665625, 1000000); 42 | Assert.assertEquals( 43 | "Encoded code should have a length of MAX_DIGIT_COUNT + 1 for the plus symbol", 44 | OpenLocationCode.MAX_DIGIT_COUNT + 1, 45 | code.length()); 46 | Assert.assertTrue("Code should be valid.", OpenLocationCode.isValidCode(code)); 47 | // Extend the code with a valid character and make sure it is still valid. 48 | String tooLongCode = code + "W"; 49 | Assert.assertTrue( 50 | "Too long code with all valid characters should be valid.", 51 | OpenLocationCode.isValidCode(tooLongCode)); 52 | // Extend the code with an invalid character and make sure it is invalid. 53 | tooLongCode = code + "U"; 54 | Assert.assertFalse( 55 | "Too long code with invalid character should be invalid.", 56 | OpenLocationCode.isValidCode(tooLongCode)); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /java/src/test/java/com/google/openlocationcode/ValidityTest.java: -------------------------------------------------------------------------------- 1 | package com.google.openlocationcode; 2 | 3 | import static java.nio.charset.StandardCharsets.UTF_8; 4 | 5 | import java.io.BufferedReader; 6 | import java.io.FileInputStream; 7 | import java.io.InputStream; 8 | import java.io.InputStreamReader; 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | import org.junit.Assert; 13 | import org.junit.Before; 14 | import org.junit.Test; 15 | import org.junit.runner.RunWith; 16 | import org.junit.runners.JUnit4; 17 | 18 | /** 19 | * Tests methods {@link com.google.openlocationcode.OpenLocationCode#isValidCode(String)}, {@link 20 | * com.google.openlocationcode.OpenLocationCode#isShortCode(String)}} and {@link 21 | * com.google.openlocationcode.OpenLocationCode#isFullCode(String)} Open Location Code. 22 | */ 23 | @RunWith(JUnit4.class) 24 | public class ValidityTest { 25 | 26 | private static class TestData { 27 | 28 | private final String code; 29 | private final boolean isValid; 30 | private final boolean isShort; 31 | private final boolean isFull; 32 | 33 | public TestData(String line) { 34 | String[] parts = line.split(","); 35 | if (parts.length != 4) { 36 | throw new IllegalArgumentException("Wrong format of testing data."); 37 | } 38 | this.code = parts[0]; 39 | this.isValid = Boolean.parseBoolean(parts[1]); 40 | this.isShort = Boolean.parseBoolean(parts[2]); 41 | this.isFull = Boolean.parseBoolean(parts[3]); 42 | } 43 | } 44 | 45 | private final List testDataList = new ArrayList<>(); 46 | 47 | @Before 48 | public void setUp() throws Exception { 49 | InputStream testDataStream = new FileInputStream(TestUtils.getTestFile("validityTests.csv")); 50 | BufferedReader reader = new BufferedReader(new InputStreamReader(testDataStream, UTF_8)); 51 | String line; 52 | while ((line = reader.readLine()) != null) { 53 | if (line.startsWith("#")) { 54 | continue; 55 | } 56 | testDataList.add(new TestData(line)); 57 | } 58 | } 59 | 60 | @Test 61 | public void testIsValid() { 62 | for (TestData testData : testDataList) { 63 | Assert.assertEquals( 64 | "Validity of code " + testData.code + " is wrong.", 65 | testData.isValid, 66 | OpenLocationCode.isValidCode(testData.code)); 67 | } 68 | } 69 | 70 | @Test 71 | public void testIsShort() { 72 | for (TestData testData : testDataList) { 73 | Assert.assertEquals( 74 | "Shortness of code " + testData.code + " is wrong.", 75 | testData.isShort, 76 | OpenLocationCode.isShortCode(testData.code)); 77 | } 78 | } 79 | 80 | @Test 81 | public void testIsFull() { 82 | for (TestData testData : testDataList) { 83 | Assert.assertEquals( 84 | "Fullness of code " + testData.code + " is wrong.", 85 | testData.isFull, 86 | OpenLocationCode.isFullCode(testData.code)); 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /js/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "env": { 3 | "browser": true, 4 | "es6": true 5 | }, 6 | "extends": "google", 7 | "globals": { 8 | "Atomics": "readonly", 9 | "SharedArrayBuffer": "readonly" 10 | }, 11 | "parserOptions": { 12 | "ecmaVersion": 2018 13 | }, 14 | "rules": { 15 | // Rules are based on the Google styleguide with the following overrides. 16 | "max-len": [2, { 17 | code: 100, 18 | tabWidth: 2, 19 | ignoreUrls: true, 20 | }], 21 | "no-var": 0, 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /js/checks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Run lint checks on files in the Javascript directory. 3 | # When running within TravisCI, post comments back to the pull request. 4 | # Also converts the test CSV files to JSON ready for the tests to execute. 5 | # Note: must run within the JS directory. 6 | if [ `basename "$PWD"` != "js" ]; then 7 | echo "$0: must be run from within the js directory!" 8 | exit 1 9 | fi 10 | 11 | # Require that the NPM install and CSV conversion commands succeed. 12 | set -e 13 | 14 | # Install all the dependencies. 15 | npm install 16 | 17 | # Convert the CSV test files to JSON and put them in the test directory for serving. 18 | go run ../test_data/csv_to_json.go --csv ../test_data/decoding.csv >test/decoding.json 19 | go run ../test_data/csv_to_json.go --csv ../test_data/encoding.csv >test/encoding.json 20 | go run ../test_data/csv_to_json.go --csv ../test_data/shortCodeTests.csv >test/shortCodeTests.json 21 | go run ../test_data/csv_to_json.go --csv ../test_data/validityTests.csv >test/validityTests.json 22 | 23 | set +e 24 | 25 | # Run the tests 26 | npm test 27 | # Save the return value for the end. 28 | RETURN=$? 29 | 30 | # Run eslint based on local installs as well as in PATH. 31 | # eslint errors will cause a build failure. 32 | ESLINT=eslint 33 | $ESLINT --version >/dev/null 2>&1 34 | if [ $? -ne 0 ]; then 35 | ESLINT=./node_modules/.bin/eslint 36 | fi 37 | 38 | $ESLINT --version >/dev/null 2>&1 39 | if [ $? -ne 0 ]; then 40 | echo "\e[1;31mCannot find eslint, check your installation\e[0m" 41 | else 42 | # Run eslint on the source file. 43 | FILE=src/openlocationcode.js 44 | LINT=`$ESLINT $FILE` 45 | if [ $? -ne 0 ]; then 46 | echo -e "\e[1;31mFile has formatting errors:\e[0m" 47 | echo "$LINT" 48 | RETURN=1 49 | if [ -v TRAVIS ]; then 50 | # On TravisCI, send a comment with the diff to the pull request. 51 | go run ../travis-utils/github_comments.go \ 52 | --comment '**File has `eslint` errors that must be fixed**:'"
$LINT
" \ 53 | --file "js/$FILE" \ 54 | --pr "$TRAVIS_PULL_REQUEST" \ 55 | --commit "$TRAVIS_PULL_REQUEST_SHA" 56 | fi 57 | fi 58 | fi 59 | exit $RETURN 60 | -------------------------------------------------------------------------------- /js/closure/BUILD: -------------------------------------------------------------------------------- 1 | # Load the necessary Closure rules 2 | load("@io_bazel_rules_closure//closure:defs.bzl", "closure_js_library", "closure_js_test") 3 | 4 | # Define the Closure library for Open Location Code 5 | closure_js_library( 6 | name = "openlocationcode_lib", 7 | srcs = ["openlocationcode.js"], 8 | convention = "GOOGLE", 9 | ) 10 | 11 | # Define the Closure test for Open Location Code 12 | closure_js_test( 13 | name = "openlocationcode_test", 14 | timeout = "short", 15 | srcs = ["openlocationcode_test.js"], 16 | data = [ 17 | "//test_data:test_data", # Reference the filegroup for test data 18 | ], 19 | entry_points = ["goog:openlocationcode_test"], 20 | deps = [ 21 | ":openlocationcode_lib", 22 | "@com_google_javascript_closure_library//closure/goog/net:eventtype", 23 | "@com_google_javascript_closure_library//closure/goog/net:xhrio", 24 | "@com_google_javascript_closure_library//closure/goog/testing:asserts", 25 | "@com_google_javascript_closure_library//closure/goog/testing:asynctestcase", 26 | "@com_google_javascript_closure_library//closure/goog/testing:testsuite", 27 | ], 28 | ) 29 | -------------------------------------------------------------------------------- /js/closure/README.md: -------------------------------------------------------------------------------- 1 | # Closure Library 2 | 3 | This is a version of the Open Location Code javascript library for use with the 4 | [Google Closure Compiler](https://github.com/google/closure-compiler). 5 | 6 | You can use it in Closure projects like this: 7 | 8 | ```javascript 9 | const openlocationcode = goog.require('google.openlocationcode'); 10 | ... 11 | var code = openlocationcode.encode(47.36628,8.52513); 12 | ``` 13 | 14 | ## Code Style And Formatting 15 | 16 | Code should be formatted according to the 17 | [Google JavaScript Style Guide](https://google.github.io/styleguide/jsguide.html). 18 | 19 | You can run checks on the code using `eslint`: 20 | 21 | ```shell 22 | cd js 23 | npm install eslint 24 | eslint closure/*js 25 | ``` 26 | 27 | If there are any syntax or style errors, it will output messages. Note that 28 | syntax or style errors will cause the TravisCI tests to **fail**. 29 | 30 | ## Building and Testing 31 | 32 | Included is a `BUILD` file that uses the [Bazel](https://bazel.build/) build system to produce a JavaScript library and to run tests. You will need to install Bazel on your system to run the tests. 33 | 34 | The tests use the [Closure Rules for Basel](https://github.com/bazelbuild/rules_closure) project although this is retrieved automatically and you don't have to install anything. 35 | 36 | The test cases have been copied from the [`test_data`](https://github.com/google/open-location-code/blob/main/test_data) directory due to restrictions on loading data files within the test runner. 37 | 38 | Run the tests from the top-level github directory with: 39 | 40 | ```shell 41 | $ bazel test js/closure:openlocationcode_test 42 | INFO: Found 1 test target... 43 | Target //js/closure:openlocationcode_test up-to-date: 44 | bazel-bin/js/closure/openlocationcode_test 45 | INFO: Elapsed time: 0.174s, Critical Path: 0.00s 46 | //js/closure:openlocationcode_test PASSED in 1.1s 47 | 48 | Executed 0 out of 1 test: 1 test passes. 49 | $ 50 | ``` 51 | 52 | -------------------------------------------------------------------------------- /js/examples/examples.css: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | Styles used by examples. 4 | */ 5 | .map_label { 6 | font-weight: 300; 7 | font-family: Roboto,arial,sans-serif; 8 | color: #37474f; 9 | font-size: 12pt; 10 | background-color: #e0e0e0; 11 | -moz-border-radius: 3px; 12 | -webkit-border-radius: 3px; 13 | border-radius: 3px; 14 | padding: 2px; 15 | } 16 | #content { 17 | height: 100%; 18 | width: 100%; 19 | } 20 | #messageBox { 21 | position: absolute; 22 | top: 30px; 23 | left: 100px; 24 | max-height: 80%; 25 | width: 30%; 26 | z-index: 100; 27 | background: white; 28 | border: 1px solid #888; 29 | overflow-y: auto; 30 | padding: 10px; 31 | -moz-border-radius: 5px; 32 | -webkit-border-radius: 5px; 33 | border-radius: 5px; 34 | } 35 | .text_input { 36 | border: 1px solid #eeeeee; 37 | padding: 5px; 38 | font-size: 100%; 39 | color: inherit; 40 | width: 100%; 41 | -moz-box-sizing: border-box; 42 | -webkit-box-sizing: border-box; 43 | box-sizing: border-box; 44 | } 45 | .button { 46 | padding: 5px; 47 | background-color: #5677fc; 48 | border: none; 49 | min-width: 100px; 50 | } 51 | .button_label { 52 | font-weight: bold; 53 | font-size: 120%; 54 | font-family: Roboto,arial,sans-serif; 55 | -webkit-font-smoothing: antialiased; 56 | color: white; 57 | opacity: 0.87; 58 | } 59 | .note_p { 60 | font-size: 80%; 61 | } 62 | .map_frame { 63 | width: 100%; 64 | height: 100%; 65 | } 66 | em { 67 | font-weight: bold; 68 | font-style: normal; 69 | } 70 | h1 { 71 | font-size: 200%; 72 | font-weight: 300; 73 | -webkit-margin-before: 0em; 74 | -webkit-margin-after: 0em; 75 | } 76 | input { 77 | font-weight: 300; 78 | font-family: Roboto,arial,sans-serif; 79 | } 80 | body, html { 81 | height: 100%; 82 | width: 100%; 83 | margin: 0; 84 | font-weight: 300; 85 | font-family: Roboto,arial,sans-serif; 86 | -webkit-font-smoothing: antialiased; 87 | } 88 | -------------------------------------------------------------------------------- /js/gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var karma = require('karma'); 3 | 4 | gulp.task('test', function(done) { 5 | var server = new karma.Server({ 6 | configFile: __dirname + '/test/karma.config.js', 7 | singleRun: true 8 | }); 9 | 10 | server.on('run_complete', function (browsers, results) { 11 | if (results.error || results.failed) { 12 | done(new Error('There are test failures')); 13 | } 14 | else { 15 | done(); 16 | } 17 | }); 18 | 19 | server.start(); 20 | }); 21 | 22 | const minify = require('gulp-minify'); 23 | gulp.task('minify', function(done) { 24 | gulp.src('src/openlocationcode.js') 25 | .pipe(minify({ 26 | ext:{ 27 | src:'.js', 28 | min:'.min.js' 29 | }, 30 | })) 31 | .pipe(gulp.dest('src')); 32 | done(); 33 | }); 34 | -------------------------------------------------------------------------------- /js/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "open-location-code", 3 | "description": "Library to convert between lat/lng and OLC codes", 4 | "version": "20250411.0.0", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/google/open-location-code.git" 8 | }, 9 | "bugs": { 10 | "url": "https://github.com/google/open-location-code/issues" 11 | }, 12 | "author": "The Open Location Code Library Authors", 13 | "license": "Apache-2.0", 14 | "homepage": "http://openlocationcode.com/", 15 | "keywords": [ 16 | "javascript", 17 | "library", 18 | "openlocationcode" 19 | ], 20 | "scripts": { 21 | "test": "gulp test" 22 | }, 23 | "devDependencies": { 24 | "eslint": "^5.16.0", 25 | "eslint-config-google": "^0.12.0", 26 | "gulp": "^4.0.1", 27 | "gulp-cli": "^2.2.0", 28 | "gulp-minify": "^3.1.0", 29 | "jasmine": "^3.4.0", 30 | "jasmine-core": "^3.4.0", 31 | "karma": "^6.4.4", 32 | "karma-chrome-launcher": "^2.2.0", 33 | "karma-jasmine": "^2.0.1", 34 | "karma-jasmine-jquery": "^0.1.1" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /js/test/karma.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function(config) { 2 | config.set({ 3 | browsers: ['ChromeHeadless'], 4 | frameworks: [ 5 | 'jasmine-jquery', 6 | 'jasmine', 7 | ], 8 | plugins: [ 9 | 'karma-chrome-launcher', 10 | 'karma-jasmine', 11 | 'karma-jasmine-jquery', 12 | ], 13 | files: [ 14 | '../src/openlocationcode.js', 15 | 'jasmine-tests.js', 16 | { pattern: '*.json', included: false, served: true} 17 | ] 18 | }); 19 | }; 20 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "open-location-code", 3 | "description": "Library to convert between lat/lng and OLC codes", 4 | "version": "1.0.0", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/google/open-location-code.git" 8 | }, 9 | "bugs": { 10 | "url": "https://github.com/google/open-location-code/issues" 11 | }, 12 | "author": "The Open Location Code Library Authors", 13 | "license": "Apache-2.0", 14 | "homepage": "http://openlocationcode.com/", 15 | "keywords": [ 16 | "javascript", 17 | "library", 18 | "openlocationcode" 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /python/.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | *.pyc 3 | openlocationcode.egg-info/* 4 | dist/* -------------------------------------------------------------------------------- /python/.style.yapf: -------------------------------------------------------------------------------- 1 | [style] 2 | # Use the Google style. 3 | based_on_style = google 4 | -------------------------------------------------------------------------------- /python/BUILD: -------------------------------------------------------------------------------- 1 | py_library( 2 | name = "openlocationcode", 3 | srcs = ["openlocationcode/openlocationcode.py"], 4 | ) 5 | 6 | py_test( 7 | name = "openlocationcode_test", 8 | python_version = "PY3", 9 | size = "small", 10 | srcs = ["openlocationcode_test.py"], 11 | data = ["//test_data:test_data"], 12 | deps = [":openlocationcode"], 13 | visibility = ["//visibility:private"] 14 | ) -------------------------------------------------------------------------------- /python/README.md: -------------------------------------------------------------------------------- 1 | # Python Library 2 | 3 | This is the Python Open Location Code library. It is tested for both python 2.7 4 | and python 3.6. 5 | 6 | ## Installing the library 7 | 8 | The python library is available on PyPi. You can install it using pip: 9 | 10 | ``` 11 | pip install openlocationcode 12 | ``` 13 | 14 | ## Formatting 15 | 16 | Code must be formatted according to the 17 | [Google Python Style Guide](http://google.github.io/styleguide/pyguide.html). 18 | 19 | You can format your code automatically using 20 | [YAPF](https://github.com/google/yapf/). 21 | 22 | ### Installing YAPF 23 | 24 | Ensure you have pip installed: 25 | 26 | ``` 27 | wget https://bootstrap.pypa.io/get-pip.py 28 | sudo python get-pip.py 29 | ``` 30 | 31 | Then install YAPF: 32 | 33 | ``` 34 | pip install --user yapf 35 | ``` 36 | 37 | ### Formatting code 38 | 39 | To format your files, just run: 40 | 41 | ``` 42 | bash format_check.sh 43 | ``` 44 | 45 | If you just want to see the changes, you can run `python -m yapf --diff *py` 46 | 47 | This script runs as part of the TravisCI tests - if files need formatting it 48 | will display the required changes **and fail the test**. 49 | 50 | 51 | ## Testing 52 | 53 | Run the unit tests and benchmarks locally with: 54 | 55 | ``` 56 | bazel test python:openlocationcode_test 57 | ``` 58 | 59 | 60 | ## Releasing to PyPi 61 | 62 | We release the python library to PyPi so users can install it using pip. 63 | 64 | Pre-reqs: 65 | 66 | ``` 67 | pip install setuptools 68 | pip install twine 69 | ``` 70 | 71 | To release a new version to PyPi, make sure you update the version number in setup.py. Then run: 72 | 73 | ``` 74 | python setup.py sdist 75 | twine upload dist/* 76 | ``` 77 | 78 | Make sure any older versions are cleared out from dist before uploading. twine will prompt you for your PyPi credentials, which will need to be a collaborator on the project. 79 | -------------------------------------------------------------------------------- /python/format_check.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Check the format of the Python source files using YAPF. 3 | 4 | python -m yapf --version >/dev/null 2>&1 5 | if [ $? -eq 1 ]; then 6 | curl -o /tmp/get-pip.py https://bootstrap.pypa.io/get-pip.py && python /tmp/get-pip.py && pip install yapf 7 | fi 8 | 9 | # Run YAPF and check for diffs. If there aren't any, we're done. 10 | DIFF=`python -m yapf --diff *py` 11 | if [ $? -eq 0 ]; then 12 | echo -e "\e[32mPython files are correctly formatted\e[30m" 13 | exit 0 14 | fi 15 | 16 | if [ -z "$TRAVIS" ]; then 17 | # Not running on TravisCI, so format the files in place. 18 | echo -e "\e[34mPython files have formatting errors -formatting in place\e[30m" 19 | python -m yapf --in-place *py 20 | else 21 | echo -e "\e[31mPython files have formatting errors\e[30m" 22 | echo -e "\e[31mThese must be corrected using format_check.sh\e[30m" 23 | echo "$DIFF" 24 | fi 25 | exit 1 26 | -------------------------------------------------------------------------------- /python/openlocationcode/__init__.py: -------------------------------------------------------------------------------- 1 | if __name__ == "__main__": 2 | from openlocationcode import * 3 | else: 4 | from .openlocationcode import * 5 | -------------------------------------------------------------------------------- /python/setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | # This call to setup() does all the work 4 | setup( 5 | name="openlocationcode", 6 | version="1.0.1", 7 | description="Python library for Open Location Code (Plus Codes)", 8 | url="https://github.com/google/open-location-code", 9 | author="Google", 10 | author_email="open-location-code@googlegroups.com", 11 | license="Apache 2.0", 12 | classifiers=[ 13 | "Programming Language :: Python :: 2.7", 14 | "Programming Language :: Python :: 3.6", 15 | ], 16 | packages=["openlocationcode"], 17 | include_package_data=True, 18 | install_requires=[], 19 | ) -------------------------------------------------------------------------------- /ruby/README.md: -------------------------------------------------------------------------------- 1 | # Plus Codes 2 | 3 | Ruby implementation of Open Location Code library. 4 | 5 | ## Contributing 6 | 7 | 1. Fork it 8 | 2. Create your feature branch (`git checkout -b my-new-feature`) 9 | 3. Commit your changes (`git commit -am 'Add some feature'`) 10 | 4. Push to the branch (`git push origin my-new-feature`) 11 | 5. Create a new Pull Request 12 | 13 | Your code must pass tests, and must be formatted with 14 | [rubocop](https://github.com/rubocop-hq/rubocop). This will check all the ruby 15 | files and print a list of corrections you need to make - it will not format your 16 | file automatically. 17 | 18 | ``` 19 | gem install rubocop 20 | rubocop --config rubocop.yml 21 | ``` 22 | 23 | If you can't run it yourself, it is run as part of the TravisCI tests. 24 | 25 | 26 | ### Testing 27 | 28 | ``` 29 | gem install test-unit 30 | ruby test/plus_codes_test.rb 31 | ``` 32 | 33 | ## Installation 34 | 35 | Add this line to your application's Gemfile: 36 | 37 | ```ruby 38 | gem 'plus_codes' 39 | ``` 40 | 41 | And then execute: 42 | 43 | $ bundle 44 | 45 | Or install it yourself as: 46 | 47 | $ gem install plus_codes 48 | 49 | ## Usage 50 | 51 | ```ruby 52 | require 'plus_codes/open_location_code' 53 | 54 | olc = PlusCodes::OpenLocationCode.new 55 | 56 | # Encodes the latitude and longitude into a Plus+Codes 57 | code = olc.encode(47.0000625,8.0000625) 58 | # => "8FVC2222+22" 59 | 60 | # Encodes any latitude and longitude into a Plus+Codes with preferred length 61 | code = olc.encode(47.0000625,8.0000625, 16) 62 | # => "8FVC2222+22GCCCCC" 63 | 64 | # Decodes a Plus+Codes back into coordinates 65 | code_area = olc.decode(code) 66 | puts code_area 67 | # => lat_lo: 47.000062496 long_lo: 8.0000625 lat_hi: 47.000062504 long_hi: 8.000062530517578 code_len: 16 68 | 69 | # Checks if a Plus+Codes is valid or not 70 | olc.valid?(code) 71 | # => true 72 | 73 | # Checks if a Plus+Codes is full or not 74 | olc.full?(code) 75 | # => true 76 | 77 | # Checks if a Plus+Codes is short or not 78 | olc.short?(code) 79 | # => false 80 | 81 | # Shorten a Plus+Codes as possible by given reference latitude and longitude 82 | olc.shorten('9C3W9QCJ+2VX', 51.3708675, -1.217765625) 83 | # => "CJ+2VX" 84 | 85 | # Extends a Plus+Codes by given reference latitude and longitude 86 | olc.recover_nearest('CJ+2VX', 51.3708675, -1.217765625) 87 | # => "9C3W9QCJ+2VX" 88 | ``` 89 | 90 | ## Contributing 91 | -------------------------------------------------------------------------------- /ruby/lib/plus_codes.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Plus+Codes is a Ruby implementation of Google Open Location Code (Plus Codes). 4 | # 5 | # @author We-Ming Wu 6 | module PlusCodes 7 | # The character set used to encode coordinates. 8 | CODE_ALPHABET = '23456789CFGHJMPQRVWX' 9 | 10 | # The character used to pad a code 11 | PADDING = '0' 12 | 13 | # A separator used to separate the code into two parts. 14 | SEPARATOR = '+' 15 | 16 | # The max number of characters can be placed before the separator. 17 | SEPARATOR_POSITION = 8 18 | 19 | # Minimum number of digits to process in a Plus Code. 20 | MIN_CODE_LENGTH = 2 21 | 22 | # Maximum number of digits to process in a Plus Code. 23 | MAX_CODE_LENGTH = 15 24 | 25 | # Maximum code length using lat/lng pair encoding. The area of such a 26 | # code is approximately 13x13 meters (at the equator), and should be suitable 27 | # for identifying buildings. This excludes prefix and separator characters. 28 | PAIR_CODE_LENGTH = 10 29 | 30 | # Inverse of the precision of the pair code section. 31 | PAIR_CODE_PRECISION = 8000 32 | 33 | # Precision of the latitude grid. 34 | LAT_GRID_PRECISION = 5**(MAX_CODE_LENGTH - PAIR_CODE_LENGTH) 35 | 36 | # Precision of the longitude grid. 37 | LNG_GRID_PRECISION = 4**(MAX_CODE_LENGTH - PAIR_CODE_LENGTH) 38 | 39 | # ASCII lookup table. 40 | DECODE = (CODE_ALPHABET.chars + [PADDING, SEPARATOR]).each_with_object( 41 | [] 42 | ) do |c, ary| 43 | ary[c.ord] = CODE_ALPHABET.index(c) 44 | ary[c.downcase.ord] = CODE_ALPHABET.index(c) 45 | ary[c.ord] ||= -1 46 | ary 47 | end.freeze 48 | end 49 | -------------------------------------------------------------------------------- /ruby/lib/plus_codes/code_area.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module PlusCodes 4 | # [CodeArea] contains coordinates of a decoded Open Location Code(Plus+Codes). 5 | # The coordinates include the latitude and longitude of the lower left and 6 | # upper right corners and the center of the bounding box for the area the 7 | # code represents. 8 | # 9 | # @author We-Ming Wu 10 | class CodeArea 11 | attr_accessor :south_latitude, :west_longitude, :latitude_height, 12 | :longitude_width, :latitude_center, :longitude_center, 13 | :code_length 14 | 15 | # Creates a [CodeArea]. 16 | # 17 | # @param south_latitude [Numeric] the latitude of the SW corner in degrees 18 | # @param west_longitude [Numeric] the longitude of the SW corner in degrees 19 | # @param latitude_height [Numeric] the height from the SW corner in degrees 20 | # @param longitude_width [Numeric] the width from the SW corner in degrees 21 | # @param code_length [Numeric] the number of significant digits in the code 22 | # @return [CodeArea] a code area which contains the coordinates 23 | def initialize(south_latitude, west_longitude, latitude_height, 24 | longitude_width, code_length) 25 | @south_latitude = south_latitude 26 | @west_longitude = west_longitude 27 | @latitude_height = latitude_height 28 | @longitude_width = longitude_width 29 | @code_length = code_length 30 | @latitude_center = south_latitude + latitude_height / 2.0 31 | @longitude_center = west_longitude + longitude_width / 2.0 32 | end 33 | 34 | def north_latitude 35 | @south_latitude + @latitude_height 36 | end 37 | 38 | def east_longitude 39 | @west_longitude + @longitude_width 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /ruby/open-location-code.gemspec: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | lib = File.expand_path('lib', __dir__) 4 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 5 | require 'date' 6 | 7 | Gem::Specification.new do |s| 8 | s.name = 'open-location-code' 9 | s.version = '1.0.4' 10 | s.authors = ['Google', 'Wei-Ming Wu'] 11 | s.date = Date.today.to_s 12 | s.email = ['open-location-code@googlegroups.com', 13 | 'wnameless@gmail.com'] 14 | s.summary = 'Ruby implementation of Open Location Code (Plus Codes)' 15 | s.description = s.summary 16 | s.homepage = 'https://github.com/google/open-location-code' 17 | s.license = 'Apache License, Version 2.0' 18 | 19 | s.files = Dir['lib/**/*'] 20 | s.test_files = Dir['test/**/*'] 21 | s.require_paths = ['lib'] 22 | s.required_ruby_version = '>= 2.6.0' 23 | 24 | s.add_development_dependency 'test-unit' 25 | end 26 | -------------------------------------------------------------------------------- /ruby/rubocop.yml: -------------------------------------------------------------------------------- 1 | # Override rubocop defaults. 2 | 3 | AllCops: 4 | TargetRubyVersion: 2.6 5 | 6 | Layout/LineLength: 7 | Enabled: true 8 | Max: 80 9 | 10 | Metrics/AbcSize: 11 | Enabled: false 12 | 13 | Metrics/ClassLength: 14 | Enabled: false 15 | 16 | Metrics/CyclomaticComplexity: 17 | Enabled: false 18 | 19 | Metrics/PerceivedComplexity: 20 | Enabled: false 21 | 22 | Metrics/MethodLength: 23 | Enabled: false 24 | 25 | Style/NumericLiterals: 26 | Enabled: false 27 | -------------------------------------------------------------------------------- /rust/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "open-location-code" 3 | description = "Library for translating between GPS coordinates (WGS84) and Open Location Code" 4 | version = "0.2.0" 5 | authors = ["James Fysh "] 6 | license = "Apache-2.0" 7 | repository = "https://github.com/google/open-location-code" 8 | keywords = ["geography", "geospatial", "gis", "gps", "olc"] 9 | exclude = ["rust.iml"] 10 | edition = "2024" 11 | 12 | [dependencies] 13 | geo = "0.30.0" 14 | 15 | [dev-dependencies] 16 | rand = "0.9.0" 17 | -------------------------------------------------------------------------------- /rust/README.md: -------------------------------------------------------------------------------- 1 | This is the Rust implementation of the Open Location Code library. 2 | 3 | # Contributing 4 | 5 | ## Code Formatting 6 | 7 | Code must be formatted with `rustfmt`. You can do this by running `cargo fmt`. 8 | 9 | The formatting will be checked in the TravisCI integration tests. If the files 10 | need formatting the tests will fail. 11 | 12 | ## Testing 13 | 14 | Test code by running `cargo test -- --nocapture`. This will run the tests 15 | including the benchmark loops. 16 | 17 | -------------------------------------------------------------------------------- /rust/src/codearea.rs: -------------------------------------------------------------------------------- 1 | use geo::Point; 2 | 3 | pub struct CodeArea { 4 | pub south: f64, 5 | pub west: f64, 6 | pub north: f64, 7 | pub east: f64, 8 | pub center: Point, 9 | pub code_length: usize, 10 | } 11 | 12 | impl CodeArea { 13 | pub fn new(south: f64, west: f64, north: f64, east: f64, code_length: usize) -> CodeArea { 14 | CodeArea { 15 | south, 16 | west, 17 | north, 18 | east, 19 | center: Point::new((west + east) / 2f64, (south + north) / 2f64), 20 | code_length, 21 | } 22 | } 23 | 24 | pub fn merge(self, other: CodeArea) -> CodeArea { 25 | CodeArea::new( 26 | self.south + other.south, 27 | self.west + other.west, 28 | self.north + other.north, 29 | self.east + other.east, 30 | self.code_length + other.code_length, 31 | ) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /rust/src/consts.rs: -------------------------------------------------------------------------------- 1 | // A separator used to break the code into two parts to aid memorability. 2 | pub const SEPARATOR: char = '+'; 3 | 4 | // The number of characters to place before the separator. 5 | pub const SEPARATOR_POSITION: usize = 8; 6 | 7 | // The character used to pad codes. 8 | pub const PADDING_CHAR: char = '0'; 9 | pub const PADDING_CHAR_STR: &str = "0"; 10 | 11 | // The character set used to encode the values. 12 | pub const CODE_ALPHABET: [char; 20] = [ 13 | '2', '3', '4', '5', '6', '7', '8', '9', 'C', 'F', 'G', 'H', 'J', 'M', 'P', 'Q', 'R', 'V', 'W', 14 | 'X', 15 | ]; 16 | 17 | // The base to use to convert numbers to/from. 18 | pub const ENCODING_BASE: usize = 20; 19 | 20 | // The maximum value for latitude in degrees. 21 | pub const LATITUDE_MAX: f64 = 90f64; 22 | 23 | // The maximum value for longitude in degrees. 24 | pub const LONGITUDE_MAX: f64 = 180f64; 25 | 26 | // Minimum number of digits to process for Plus Codes. 27 | pub const MIN_CODE_LENGTH: usize = 2; 28 | 29 | // Maximum number of digits to process for Plus Codes. 30 | pub const MAX_CODE_LENGTH: usize = 15; 31 | 32 | // Maximum code length using lat/lng pair encoding. The area of such a 33 | // code is approximately 13x13 meters (at the equator), and should be suitable 34 | // for identifying buildings. This excludes prefix and separator characters. 35 | pub const PAIR_CODE_LENGTH: usize = 10; 36 | 37 | // Digits in the grid encoding.. 38 | pub const GRID_CODE_LENGTH: usize = 5; 39 | 40 | // The resolution values in degrees for each position in the lat/lng pair 41 | // encoding. These give the place value of each position, and therefore the 42 | // dimensions of the resulting area. 43 | pub const PAIR_RESOLUTIONS: [f64; 5] = [20.0f64, 1.0f64, 0.05f64, 0.0025f64, 0.000125f64]; 44 | 45 | // Number of columns in the grid refinement method. 46 | pub const GRID_COLUMNS: usize = 4; 47 | 48 | // Number of rows in the grid refinement method. 49 | pub const GRID_ROWS: usize = 5; 50 | 51 | // Minimum length of a code that can be shortened. 52 | pub const MIN_TRIMMABLE_CODE_LEN: usize = 6; 53 | 54 | // What to multiply latitude degrees by to get an integer value. There are three pairs representing 55 | // decimal digits, and five digits in the grid. 56 | pub const LAT_INTEGER_MULTIPLIER: i64 = (ENCODING_BASE 57 | * ENCODING_BASE 58 | * ENCODING_BASE 59 | * GRID_ROWS 60 | * GRID_ROWS 61 | * GRID_ROWS 62 | * GRID_ROWS 63 | * GRID_ROWS) as i64; 64 | 65 | // What to multiply longitude degrees by to get an integer value. There are three pairs representing 66 | // decimal digits, and five digits in the grid. 67 | pub const LNG_INTEGER_MULTIPLIER: i64 = (ENCODING_BASE 68 | * ENCODING_BASE 69 | * ENCODING_BASE 70 | * GRID_COLUMNS 71 | * GRID_COLUMNS 72 | * GRID_COLUMNS 73 | * GRID_COLUMNS 74 | * GRID_COLUMNS) as i64; 75 | -------------------------------------------------------------------------------- /rust/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate geo; 2 | 3 | mod codearea; 4 | mod consts; 5 | mod interface; 6 | mod private; 7 | 8 | pub use codearea::CodeArea; 9 | pub use interface::{ 10 | decode, encode, encode_integers, is_full, is_short, is_valid, point_to_integers, 11 | recover_nearest, shorten, 12 | }; 13 | -------------------------------------------------------------------------------- /rust/src/private.rs: -------------------------------------------------------------------------------- 1 | use geo::Point; 2 | 3 | use crate::{ 4 | consts::{ 5 | CODE_ALPHABET, ENCODING_BASE, GRID_ROWS, LATITUDE_MAX, LONGITUDE_MAX, PAIR_CODE_LENGTH, 6 | }, 7 | encode, 8 | }; 9 | 10 | pub fn code_value(chr: char) -> usize { 11 | // We assume this function is only called by other functions that have 12 | // already ensured that the characters in the passed-in code are all valid 13 | // and have all been "treated" (upper-cased, padding and '+' stripped) 14 | CODE_ALPHABET.iter().position(|&x| x == chr).unwrap() 15 | } 16 | 17 | pub fn normalize_longitude(value: f64) -> f64 { 18 | let mut result: f64 = value; 19 | while result >= LONGITUDE_MAX { 20 | result -= LONGITUDE_MAX * 2f64; 21 | } 22 | while result < -LONGITUDE_MAX { 23 | result += LONGITUDE_MAX * 2f64; 24 | } 25 | result 26 | } 27 | 28 | pub fn clip_latitude(latitude_degrees: f64) -> f64 { 29 | latitude_degrees.min(LATITUDE_MAX).max(-LATITUDE_MAX) 30 | } 31 | 32 | pub fn compute_latitude_precision(code_length: usize) -> f64 { 33 | if code_length <= PAIR_CODE_LENGTH { 34 | return (ENCODING_BASE as f64).powf((code_length as f64 / -2f64 + 2f64).floor()); 35 | } 36 | (ENCODING_BASE as f64).powf(-3f64) 37 | / GRID_ROWS.pow((code_length - PAIR_CODE_LENGTH) as u32) as f64 38 | } 39 | 40 | pub fn prefix_by_reference(pt: Point, code_length: usize) -> String { 41 | let precision = compute_latitude_precision(code_length); 42 | 43 | let (lng, lat) = pt.x_y(); 44 | 45 | let mut code = encode( 46 | Point::new( 47 | (lng / precision).floor() * precision, 48 | (lat / precision).floor() * precision, 49 | ), 50 | PAIR_CODE_LENGTH, 51 | ); 52 | code.drain(code_length..); 53 | code 54 | } 55 | -------------------------------------------------------------------------------- /rust/tests/csv_reader.rs: -------------------------------------------------------------------------------- 1 | use std::env::current_dir; 2 | use std::fs::File; 3 | use std::io::{BufRead, BufReader, Lines}; 4 | 5 | pub struct CSVReader { 6 | iter: Lines>, 7 | } 8 | 9 | impl CSVReader { 10 | pub fn new(csv_name: &str) -> CSVReader { 11 | // Assumes we're called from /rust 12 | let project_root = current_dir().unwrap(); 13 | let olc_root = project_root.parent().unwrap(); 14 | let csv_path = olc_root.join("test_data").join(csv_name); 15 | CSVReader { 16 | iter: BufReader::new(File::open(csv_path).unwrap()).lines(), 17 | } 18 | } 19 | } 20 | 21 | impl Iterator for CSVReader { 22 | type Item = String; 23 | 24 | fn next(&mut self) -> Option { 25 | // Iterate lines in the CSV file, dropping empty & comment lines 26 | while let Some(Ok(s)) = self.iter.next() { 27 | if s.is_empty() || s.starts_with("#") { 28 | continue; 29 | } 30 | return Some(s); 31 | } 32 | None 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /test_data/BUILD: -------------------------------------------------------------------------------- 1 | filegroup( 2 | name = "test_data", 3 | srcs = glob(["*.csv"]), 4 | visibility = ["//visibility:public"], 5 | ) 6 | -------------------------------------------------------------------------------- /test_data/csv_to_json.go: -------------------------------------------------------------------------------- 1 | // Package main converts test files from CSV to JSON. 2 | // This can be used by e.g. JS tests that cannot read data as CSV. 3 | // Example: 4 | // go run csv_to_json.go --csv encoding.csv >js/test/encoding.json 5 | package main 6 | 7 | import ( 8 | "bufio" 9 | "encoding/csv" 10 | "flag" 11 | "fmt" 12 | "log" 13 | "os" 14 | "strconv" 15 | "strings" 16 | ) 17 | 18 | var ( 19 | csvPtr = flag.String("csv", "", "CSV file") 20 | ) 21 | 22 | func main() { 23 | flag.Parse() 24 | if *csvPtr == "" { 25 | log.Fatal("--csv is required") 26 | } 27 | csvFile, err := os.Open(*csvPtr) 28 | if err != nil { 29 | log.Fatal(err) 30 | } 31 | reader := csv.NewReader(bufio.NewReader(csvFile)) 32 | reader.Comment = '#' 33 | records, err := reader.ReadAll() 34 | if err != nil { 35 | log.Fatal(err) 36 | } 37 | var formatted []string 38 | for i := 0; i < len(records); i++ { 39 | for j := 0; j < len(records[i]); j++ { 40 | // Anything that can't be parsed as a float is a string and needs quotes. 41 | if _, err := strconv.ParseFloat(records[i][j], 64); err != nil { 42 | records[i][j] = "\"" + records[i][j] + "\"" 43 | } 44 | } 45 | formatted = append(formatted, "["+strings.Join(records[i], ",")+"]") 46 | } 47 | fmt.Printf("[\n%s\n]\n", strings.Join(formatted, ",\n")) 48 | } 49 | -------------------------------------------------------------------------------- /test_data/shortCodeTests.csv: -------------------------------------------------------------------------------- 1 | # Test shortening and extending codes. 2 | # 3 | # Format: 4 | # full code,lat,lng,shortcode,test_type 5 | # test_type is R for recovery only, S for shorten only, or B for both. 6 | 9C3W9QCJ+2VX,51.3701125,-1.217765625,+2VX,B 7 | # Adjust so we can't trim by 8 (+/- .000755) 8 | 9C3W9QCJ+2VX,51.3708675,-1.217765625,CJ+2VX,B 9 | 9C3W9QCJ+2VX,51.3693575,-1.217765625,CJ+2VX,B 10 | 9C3W9QCJ+2VX,51.3701125,-1.218520625,CJ+2VX,B 11 | 9C3W9QCJ+2VX,51.3701125,-1.217010625,CJ+2VX,B 12 | # Adjust so we can't trim by 6 (+/- .0151) 13 | 9C3W9QCJ+2VX,51.3852125,-1.217765625,9QCJ+2VX,B 14 | 9C3W9QCJ+2VX,51.3550125,-1.217765625,9QCJ+2VX,B 15 | 9C3W9QCJ+2VX,51.3701125,-1.232865625,9QCJ+2VX,B 16 | 9C3W9QCJ+2VX,51.3701125,-1.202665625,9QCJ+2VX,B 17 | # Added to detect error in recoverNearest functionality 18 | 8FJFW222+,42.899,9.012,22+,B 19 | 796RXG22+,14.95125,-23.5001,22+,B 20 | # Reference location is in the 4 digit cell to the south. 21 | 8FVC2GGG+GG,46.976,8.526,2GGG+GG,B 22 | # Reference location is in the 4 digit cell to the north. 23 | 8FRCXGGG+GG,47.026,8.526,XGGG+GG,B 24 | # Reference location is in the 4 digit cell to the east. 25 | 8FR9GXGG+GG,46.526,8.026,GXGG+GG,B 26 | # Reference location is in the 4 digit cell to the west. 27 | 8FRCG2GG+GG,46.526,7.976,G2GG+GG,B 28 | # Added to detect errors recovering codes near the poles. 29 | # This tests recovery function, but these codes won't shorten. 30 | CFX22222+22,89.6,0.0,2222+22,R 31 | 2CXXXXXX+XX,-81.0,0.0,XXXXXX+XX,R 32 | # Recovered full codes should be the full code 33 | 8FRCG2GG+GG,46.526,7.976,8FRCG2GG+GG,R 34 | # Recovered full codes should be the uppercased full code 35 | 8FRCG2GG+GG,46.526,7.976,8frCG2GG+gG,R 36 | -------------------------------------------------------------------------------- /test_data/validityTests.csv: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # 3 | # Test data for validity tests. 4 | # Format of each line is: 5 | # code,isValid,isShort,isFull 6 | # 7 | ################################################################################ 8 | # 9 | # Valid full codes: 10 | # 11 | ################################################################################ 12 | 8FWC2345+G6,true,false,true 13 | 8FWC2345+G6G,true,false,true 14 | 8fwc2345+,true,false,true 15 | 8FWCX400+,true,false,true 16 | 84000000+,true,false,true 17 | ################################################################################ 18 | # 19 | # Valid short codes: 20 | # 21 | ################################################################################ 22 | WC2345+G6g,true,true,false 23 | 2345+G6,true,true,false 24 | 45+G6,true,true,false 25 | +G6,true,true,false 26 | ################################################################################ 27 | # 28 | # Invalid codes 29 | # 30 | ################################################################################ 31 | G+,false,false,false 32 | +,false,false,false 33 | 8FWC2345+G,false,false,false 34 | 8FWC2_45+G6,false,false,false 35 | 8FWC2η45+G6,false,false,false 36 | 8FWC2345+G6+,false,false,false 37 | 8FWC2345G6+,false,false,false 38 | 8FWC2300+G6,false,false,false 39 | WC2300+G6g,false,false,false 40 | WC2345+G,false,false,false 41 | WC2300+,false,false,false 42 | 84900000+,false,false,false 43 | ################################################################################ 44 | # 45 | # Validate that codes at and exceeding 15 digits are still valid when all their 46 | # digits are valid, and invalid when not. 47 | # 48 | ################################################################################ 49 | 849VGJQF+VX7QR3J,true,false,true 50 | 849VGJQF+VX7QR3U,false,false,false 51 | 849VGJQF+VX7QR3JW,true,false,true 52 | 849VGJQF+VX7QR3JU,false,false,false 53 | -------------------------------------------------------------------------------- /tile_server/gridserver/geojson_test.go: -------------------------------------------------------------------------------- 1 | package gridserver 2 | 3 | import ( 4 | "encoding/json" 5 | "os" 6 | "testing" 7 | 8 | log "github.com/golang/glog" 9 | "github.com/google/go-cmp/cmp" 10 | "github.com/paulmach/orb/geojson" 11 | ) 12 | 13 | const ( 14 | testDataPath = "./testdata/" 15 | ) 16 | 17 | func readTestData(p string) []byte { 18 | d, err := os.ReadFile(p) 19 | if err != nil { 20 | log.Fatal(err) 21 | } 22 | return d 23 | } 24 | 25 | func TestGeoJSON(t *testing.T) { 26 | var tests = []struct { 27 | x, y, z int 28 | opts *TileOptions 29 | testFile string 30 | }{ 31 | {x: 17, y: 19, z: 5, testFile: testDataPath + "5_17_19.json"}, 32 | { 33 | x: 17, y: 19, z: 5, 34 | opts: &TileOptions{Format: JSONTile, LineColor: lineColor, LabelColor: labelColor, Projection: NewMercatorTMS(), ZoomAdjust: 2}, 35 | testFile: testDataPath + "5_17_19_zoom_2.json", 36 | }, 37 | { 38 | x: 1098232, y: 1362659, z: 21, 39 | opts: &TileOptions{Format: JSONTile, LineColor: lineColor, LabelColor: labelColor, Projection: NewMercatorTMS(), ZoomAdjust: 0}, 40 | testFile: testDataPath + "21_1098232_1362659.json", 41 | }, 42 | { 43 | x: 17, y: 19, z: 5, 44 | opts: &TileOptions{Format: JSONTile, LineColor: lineColor, LabelColor: labelColor, Projection: NewGeodeticTMS(), ZoomAdjust: 0}, 45 | testFile: testDataPath + "5_17_19_geodetic.json", 46 | }, 47 | { 48 | x: 1098232, y: 1362659, z: 21, 49 | opts: &TileOptions{Format: JSONTile, LineColor: lineColor, LabelColor: labelColor, Projection: NewGeodeticTMS(), ZoomAdjust: 0}, 50 | testFile: testDataPath + "21_1098232_1362659_geodetic.json", 51 | }, 52 | } 53 | for n, test := range tests { 54 | // Read the test data, convert to struct. 55 | want := &geojson.FeatureCollection{} 56 | if err := json.Unmarshal(readTestData(test.testFile), want); err != nil { 57 | t.Errorf("Test %d: data unmarshal failed: %v", n, err) 58 | } 59 | // Make the tile reference and get the geojson struct. 60 | tr := MakeTileRef(test.x, test.y, test.z, test.opts) 61 | got, err := tr.GeoJSON() 62 | if err != nil { 63 | t.Errorf("Test %d: GeoJSON generation failed: %v", n, err) 64 | } 65 | if !cmp.Equal(got, want) { 66 | if blob, err := got.MarshalJSON(); err != nil { 67 | t.Errorf("Test %d: got %v, want %v", n, got, want) 68 | } else { 69 | t.Errorf("Test %d: got %s", n, string(blob)) 70 | } 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /tile_server/gridserver/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/google/open-location-code/tile_server/gridserver 2 | 3 | go 1.17 4 | 5 | require ( 6 | github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 7 | github.com/golang/glog v1.2.4 8 | github.com/google/go-cmp v0.6.0 9 | github.com/google/open-location-code/go v0.0.0-20210504205230-1796878d947c 10 | github.com/paulmach/orb v0.11.1 11 | golang.org/x/image v0.18.0 12 | ) 13 | 14 | require ( 15 | github.com/gogo/protobuf v1.3.2 // indirect 16 | github.com/paulmach/protoscan v0.2.1 // indirect 17 | go.mongodb.org/mongo-driver v1.11.4 // indirect 18 | ) 19 | -------------------------------------------------------------------------------- /tile_server/gridserver/image_test.go: -------------------------------------------------------------------------------- 1 | package gridserver 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | ) 7 | 8 | func TestImage(t *testing.T) { 9 | var tests = []struct { 10 | x, y, z int 11 | opts *TileOptions 12 | testFile string 13 | }{ 14 | { 15 | x: 17, y: 19, z: 5, 16 | testFile: testDataPath + "5_17_19.png", 17 | }, 18 | { 19 | x: 17, y: 19, z: 5, 20 | opts: &TileOptions{Format: ImageTile, LineColor: white, LabelColor: white, Projection: NewMercatorTMS(), ZoomAdjust: 2}, 21 | testFile: testDataPath + "5_17_19_white_zoom_2.png", 22 | }, 23 | { 24 | x: 1098232, y: 1362659, z: 21, testFile: testDataPath + "21_1098232_1362659.png", 25 | }, 26 | { 27 | x: 1098232, y: 1362659, z: 21, 28 | opts: &TileOptions{Format: ImageTile, LineColor: lineColor, LabelColor: labelColor, Projection: NewGeodeticTMS(), ZoomAdjust: 0}, 29 | testFile: testDataPath + "21_1098232_1362659_geodetic.png", 30 | }, 31 | } 32 | for n, td := range tests { 33 | want := readTestData(td.testFile) 34 | tr := MakeTileRef(td.x, td.y, td.z, td.opts) 35 | got, err := tr.Image() 36 | if err != nil { 37 | t.Errorf("Test %d: image failed: %v", n, err) 38 | } 39 | if !bytes.Equal(got, want) { 40 | t.Errorf("Test %d: got image != want image", n) 41 | } 42 | 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /tile_server/gridserver/mvt.go: -------------------------------------------------------------------------------- 1 | package gridserver 2 | 3 | import ( 4 | log "github.com/golang/glog" 5 | "github.com/paulmach/orb/encoding/mvt" 6 | "github.com/paulmach/orb/maptile" 7 | ) 8 | 9 | const ( 10 | layerName = "grid" 11 | layerVersion = 2 12 | layerExtent = 4096 13 | ) 14 | 15 | // MVT returns a Mapbox Vector Tile (MVT) marshalled as bytes. 16 | func (t *TileRef) MVT() ([]byte, error) { 17 | log.Infof("Producing mvt for tile z/x/y %v/%v/%v (%s)", t.Z, t.X, t.Y, t.Path()) 18 | gj, err := t.GeoJSON() 19 | if err != nil { 20 | return nil, err 21 | } 22 | 23 | layer := &mvt.Layer{ 24 | Name: layerName, 25 | Version: layerVersion, 26 | Extent: layerExtent, 27 | Features: gj.Features, 28 | } 29 | 30 | // Since GeoJSON stores geometries in latitude and longitude (WGS84), 31 | // we only need to project the coordinates if the desired output projection is Mercator. 32 | if t.Options.Projection.String() == "mercator" { 33 | // Convert TMS coordinates to WMS coordinates 34 | wmsY := (1 << uint(t.Z)) - t.Y - 1 35 | layer.ProjectToTile(maptile.New(uint32(t.X), uint32(wmsY), maptile.Zoom(t.Z))) 36 | } 37 | 38 | data, err := mvt.Marshal(mvt.Layers{layer}) 39 | if err != nil { 40 | return nil, err 41 | } 42 | return data, nil 43 | } 44 | -------------------------------------------------------------------------------- /tile_server/gridserver/mvt_test.go: -------------------------------------------------------------------------------- 1 | package gridserver 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | ) 7 | 8 | func TestMVT(t *testing.T) { 9 | var tests = []struct { 10 | x, y, z int 11 | opts *TileOptions 12 | testFile string 13 | }{ 14 | {x: 17, y: 19, z: 5, testFile: testDataPath + "5_17_19.mvt"}, 15 | { 16 | x: 17, y: 19, z: 5, 17 | opts: &TileOptions{Format: VectorTile, LineColor: lineColor, LabelColor: labelColor, Projection: NewMercatorTMS(), ZoomAdjust: 2}, 18 | testFile: testDataPath + "5_17_19_zoom_2.mvt", 19 | }, 20 | { 21 | x: 1098232, y: 1362659, z: 21, 22 | opts: &TileOptions{Format: VectorTile, LineColor: lineColor, LabelColor: labelColor, Projection: NewMercatorTMS(), ZoomAdjust: 0}, 23 | testFile: testDataPath + "21_1098232_1362659.mvt", 24 | }, 25 | { 26 | x: 17, y: 19, z: 5, 27 | opts: &TileOptions{Format: VectorTile, LineColor: lineColor, LabelColor: labelColor, Projection: NewGeodeticTMS(), ZoomAdjust: 0}, 28 | testFile: testDataPath + "5_17_19_geodetic.mvt", 29 | }, 30 | { 31 | x: 1098232, y: 1362659, z: 21, 32 | opts: &TileOptions{Format: VectorTile, LineColor: lineColor, LabelColor: labelColor, Projection: NewGeodeticTMS(), ZoomAdjust: 0}, 33 | testFile: testDataPath + "21_1098232_1362659_geodetic.mvt", 34 | }, 35 | } 36 | for n, td := range tests { 37 | want := readTestData(td.testFile) 38 | tr := MakeTileRef(td.x, td.y, td.z, td.opts) 39 | got, err := tr.MVT() 40 | if err != nil { 41 | t.Errorf("Test %d: MVT failed: %v", n, err) 42 | } 43 | if !bytes.Equal(got, want) { 44 | t.Errorf("Test %d: got MVT != want MVT", n) 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /tile_server/gridserver/projection_test.go: -------------------------------------------------------------------------------- 1 | package gridserver 2 | 3 | import ( 4 | "math" 5 | "testing" 6 | ) 7 | 8 | func TestTileLatLngBounds(t *testing.T) { 9 | var tests = []struct { 10 | x, y, z int 11 | p Projection 12 | sw, ne *LatLng 13 | }{ 14 | { 15 | x: 0, y: 0, z: 1, 16 | p: NewMercatorTMS(), 17 | sw: &LatLng{-85.051128, -180}, 18 | ne: &LatLng{0, 0}, 19 | }, 20 | { 21 | x: 0, y: 0, z: 0, 22 | p: NewGeodeticTMS(), 23 | sw: &LatLng{-90, -180}, 24 | ne: &LatLng{90, 0}, 25 | }, 26 | { 27 | x: 1, y: 0, z: 0, 28 | p: NewGeodeticTMS(), 29 | sw: &LatLng{-90, 0}, 30 | ne: &LatLng{90, 180}, 31 | }, 32 | } 33 | for _, test := range tests { 34 | latlo, lnglo, lathi, lnghi := test.p.TileLatLngBounds(test.x, test.y, test.z) 35 | if !floatEquals(latlo, test.sw.Lat, 1e-6) || 36 | !floatEquals(lnglo, test.sw.Lng, 1e-6) || 37 | !floatEquals(lathi, test.ne.Lat, 1e-6) || 38 | !floatEquals(lnghi, test.ne.Lng, 1e-6) { 39 | t.Errorf( 40 | "TileLatLngBounds(%v, %v, %v): wanted %s - %s, got %v,%v - %v,%v", 41 | test.x, test.y, test.z, test.sw, test.ne, latlo, lnglo, lathi, lnghi) 42 | } 43 | } 44 | } 45 | 46 | func TestLatLngToRaster(t *testing.T) { 47 | var tests = []struct { 48 | p Projection 49 | lat, lng, zoom float64 50 | x, y float64 51 | }{ 52 | { 53 | p: NewMercatorTMS(), 54 | lat: 90, lng: -180, zoom: 1, 55 | x: 0, y: -2786.073317, 56 | }, 57 | { 58 | p: NewMercatorTMS(), 59 | lat: 85.05112875, lng: -180, zoom: 1, 60 | x: 0, y: 0, 61 | }, 62 | { 63 | p: NewMercatorTMS(), 64 | lat: 0, lng: 0, zoom: 1, 65 | x: 256, y: 256, 66 | }, 67 | { 68 | p: NewMercatorTMS(), 69 | lat: -85.05112875, lng: 180, zoom: 1, 70 | x: 512, y: 512, 71 | }, 72 | { 73 | p: NewMercatorTMS(), 74 | lat: -90, lng: 180, zoom: 1, 75 | x: 512, y: 3298.073317, 76 | }, 77 | } 78 | for _, test := range tests { 79 | gotx, goty := test.p.LatLngToRaster(test.lat, test.lng, test.zoom) 80 | if !floatEquals(gotx, test.x, 1e-6) || 81 | !floatEquals(goty, test.y, 1e-6) { 82 | t.Errorf( 83 | "TestLatLngToRaster(%v, %v, %v): wanted %v,%v, got %v,%v diff (%v, %v)", 84 | test.lat, test.lng, test.zoom, test.x, test.y, gotx, goty, 85 | math.Abs(test.x-gotx), math.Abs(test.y-goty)) 86 | } 87 | } 88 | } 89 | 90 | func floatEquals(a, b, margin float64) bool { 91 | return math.Abs(a-b) < margin 92 | } 93 | -------------------------------------------------------------------------------- /tile_server/gridserver/testdata/21_1098232_1362659.mvt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/open-location-code/3cf9f806af4d1c47388badea615064544cec94f5/tile_server/gridserver/testdata/21_1098232_1362659.mvt -------------------------------------------------------------------------------- /tile_server/gridserver/testdata/21_1098232_1362659.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/open-location-code/3cf9f806af4d1c47388badea615064544cec94f5/tile_server/gridserver/testdata/21_1098232_1362659.png -------------------------------------------------------------------------------- /tile_server/gridserver/testdata/21_1098232_1362659_geodetic.json: -------------------------------------------------------------------------------- 1 | { 2 | "type":"FeatureCollection", 3 | "features":[ 4 | { 5 | "type":"Feature", 6 | "geometry":{ 7 | "type":"Polygon", 8 | "coordinates":[ 9 | [ 10 | [ 11 | -85.738, 12 | 26.957875 13 | ], 14 | [ 15 | -85.738, 16 | 26.958000000000002 17 | ], 18 | [ 19 | -85.737875, 20 | 26.958000000000002 21 | ], 22 | [ 23 | -85.737875, 24 | 26.957875 25 | ] 26 | ] 27 | ] 28 | }, 29 | "properties":{ 30 | "area_code":"76RP", 31 | "global_code":"76RPX756+5R", 32 | "local_code":"X756+5R", 33 | "name":"76RPX756+5R" 34 | } 35 | }, 36 | { 37 | "type":"Feature", 38 | "geometry":{ 39 | "type":"Polygon", 40 | "coordinates":[ 41 | [ 42 | [ 43 | -85.738, 44 | 26.958000000000002 45 | ], 46 | [ 47 | -85.738, 48 | 26.958125000000003 49 | ], 50 | [ 51 | -85.737875, 52 | 26.958125000000003 53 | ], 54 | [ 55 | -85.737875, 56 | 26.958000000000002 57 | ] 58 | ] 59 | ] 60 | }, 61 | "properties":{ 62 | "area_code":"76RP", 63 | "global_code":"76RPX756+6R", 64 | "local_code":"X756+6R", 65 | "name":"76RPX756+6R" 66 | } 67 | } 68 | ] 69 | } 70 | -------------------------------------------------------------------------------- /tile_server/gridserver/testdata/21_1098232_1362659_geodetic.mvt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/open-location-code/3cf9f806af4d1c47388badea615064544cec94f5/tile_server/gridserver/testdata/21_1098232_1362659_geodetic.mvt -------------------------------------------------------------------------------- /tile_server/gridserver/testdata/21_1098232_1362659_geodetic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/open-location-code/3cf9f806af4d1c47388badea615064544cec94f5/tile_server/gridserver/testdata/21_1098232_1362659_geodetic.png -------------------------------------------------------------------------------- /tile_server/gridserver/testdata/5_17_19.json: -------------------------------------------------------------------------------- 1 | { 2 | "type":"FeatureCollection", 3 | "features":[ 4 | { 5 | "type":"Feature", 6 | "geometry":{"type":"Polygon","coordinates":[[[0,30],[0,50],[20,50],[20,30]]]}, 7 | "properties":{"global_code":"8F000000+","name":"8F"} 8 | }, 9 | { 10 | "type":"Feature", 11 | "geometry":{"type":"Polygon","coordinates":[[[20,30],[20,50],[40,50],[40,30]]]}, 12 | "properties":{"global_code":"8G000000+","name":"8G"} 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /tile_server/gridserver/testdata/5_17_19.mvt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/open-location-code/3cf9f806af4d1c47388badea615064544cec94f5/tile_server/gridserver/testdata/5_17_19.mvt -------------------------------------------------------------------------------- /tile_server/gridserver/testdata/5_17_19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/open-location-code/3cf9f806af4d1c47388badea615064544cec94f5/tile_server/gridserver/testdata/5_17_19.png -------------------------------------------------------------------------------- /tile_server/gridserver/testdata/5_17_19_geodetic.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "FeatureCollection", "features": [ 3 | { 4 | "type": "Feature", 5 | "geometry": { 6 | "type": "Polygon", 7 | "coordinates": [[[-100, 10], [-100, 30], [-80, 30], [-80, 10]]] 8 | }, 9 | "properties": {"global_code": "76000000+", "name": "76"} 10 | }, 11 | { 12 | "type": "Feature", 13 | "geometry": { 14 | "type": "Polygon", 15 | "coordinates": [[[-80, 10], [-80, 30], [-60, 30], [-60, 10]]] 16 | }, 17 | "properties": {"global_code": "77000000+", "name": "77"} 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /tile_server/gridserver/testdata/5_17_19_geodetic.mvt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/open-location-code/3cf9f806af4d1c47388badea615064544cec94f5/tile_server/gridserver/testdata/5_17_19_geodetic.mvt -------------------------------------------------------------------------------- /tile_server/gridserver/testdata/5_17_19_white_zoom_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/open-location-code/3cf9f806af4d1c47388badea615064544cec94f5/tile_server/gridserver/testdata/5_17_19_white_zoom_2.png -------------------------------------------------------------------------------- /tile_server/gridserver/testdata/5_17_19_zoom_2.mvt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/open-location-code/3cf9f806af4d1c47388badea615064544cec94f5/tile_server/gridserver/testdata/5_17_19_zoom_2.mvt -------------------------------------------------------------------------------- /tile_server/main.go: -------------------------------------------------------------------------------- 1 | // main starts the server. 2 | package main 3 | 4 | import ( 5 | "flag" 6 | "fmt" 7 | "net/http" 8 | 9 | log "github.com/golang/glog" 10 | // Use the production gridserver. In development, change to just "./gridserver". 11 | "github.com/google/open-location-code/tile_server/gridserver" 12 | "golang.org/x/image/font/gofont/goregular" 13 | ) 14 | 15 | var ( 16 | port = flag.Int("port", 8080, "Port to run the server on") 17 | ) 18 | 19 | const ( 20 | originHeader = "Access-Control-Allow-Origin" 21 | contentTypeHeader = "Content-Type" 22 | contentTypeGeoJSON = "application/vnd.geo+json" 23 | contentTypePNG = "image/png" 24 | contentTypeMVT = "application/vnd.mapbox-vector-tile" 25 | ) 26 | 27 | func init() { 28 | flag.Parse() 29 | } 30 | 31 | func main() { 32 | log.Infof("Starting...") 33 | gridserver.SetImageFont(goregular.TTF) 34 | http.HandleFunc("/", Handler) 35 | log.Infof("Ready") 36 | if err := http.ListenAndServe(fmt.Sprintf(":%v", *port), nil); err != nil { 37 | panic(err) 38 | } 39 | } 40 | 41 | // Handler processes a tileReq for a single tile and writes either the GeoJSON or image as a response. 42 | func Handler(w http.ResponseWriter, r *http.Request) { 43 | if err := r.ParseForm(); err != nil { 44 | log.Errorf("Bad http request: %v: %v", r.URL, err) 45 | http.Error(w, err.Error(), 400) 46 | return 47 | } 48 | tile, err := gridserver.Parse(r) 49 | if err != nil { 50 | log.Errorf("Bad tile request: %v: %v", r.URL, err) 51 | http.Error(w, err.Error(), 400) 52 | return 53 | } 54 | w.Header().Set(originHeader, "*") 55 | 56 | // Response content. May be a tile or JSON. 57 | ctype := contentTypeGeoJSON 58 | blob := []byte("") 59 | if tile.Options.Format == gridserver.JSONTile { 60 | json, err := tile.GeoJSON() 61 | if err != nil { 62 | log.Errorf("Error producing geojson tile: %v", err) 63 | http.Error(w, err.Error(), 500) 64 | return 65 | } 66 | if blob, err = json.MarshalJSON(); err != nil { 67 | log.Errorf("Error marshaling geojson tile: %v", err) 68 | http.Error(w, err.Error(), 500) 69 | return 70 | } 71 | } else if tile.Options.Format == gridserver.ImageTile { 72 | ctype = contentTypePNG 73 | if blob, err = tile.Image(); err != nil { 74 | log.Errorf("Error producing image tile: %v", err) 75 | http.Error(w, err.Error(), 500) 76 | return 77 | } 78 | } else if tile.Options.Format == gridserver.VectorTile { 79 | ctype = contentTypeMVT 80 | if blob, err = tile.MVT(); err != nil { 81 | log.Errorf("Error producing mapbox vector tile: %v", err) 82 | http.Error(w, err.Error(), 500) 83 | return 84 | } 85 | } 86 | w.Header().Set(contentTypeHeader, ctype) 87 | w.Write(blob) 88 | } 89 | --------------------------------------------------------------------------------