├── .appveyor.yml ├── .gitignore ├── .gitmodules ├── .travis.yml ├── LICENSE.md ├── README.md ├── addon_config.mk ├── example ├── addons.make ├── bin │ └── data │ │ ├── provider.json │ │ └── ssl │ │ └── cacert.pem └── src │ ├── main.cpp │ ├── ofApp.cpp │ └── ofApp.h ├── libs └── ofxMaps │ ├── includes │ └── ofx │ │ └── Maps │ │ ├── AbstractMapTypes.h │ │ ├── BaseProjection.h │ │ ├── MBTilesCache.h │ │ ├── MapNavigator.h │ │ ├── MapTileLayer.h │ │ ├── MapTileProvider.h │ │ ├── MapTileSet.h │ │ ├── SphericalMercatorProjection.h │ │ ├── Tile.h │ │ ├── TileCoordinate.h │ │ ├── TileKey.h │ │ └── TileTemplate.h │ └── src │ ├── BaseProjection.cpp │ ├── MBTilesCache.cpp │ ├── MapNavigator.cpp │ ├── MapTileLayer.cpp │ ├── MapTileProvider.cpp │ ├── MapTileSet.cpp │ ├── SphericalMercatorProjection.cpp │ ├── Tile.cpp │ ├── TileCoordinate.cpp │ ├── TileKey.cpp │ └── TileTemplate.cpp ├── scripts ├── bootstrap.sh └── ci │ └── install.sh └── src └── ofxMaps.h /.appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 1.0.{build} 2 | os: Visual Studio 2015 RC 3 | 4 | environment: 5 | global: 6 | APPVEYOR_OS_NAME: windows 7 | matrix: 8 | #MSYS2 Building 9 | - platform: x86 10 | BUILDER: MSYS2 11 | 12 | #VisualStudio Building 13 | - platform: x86 14 | BUILDER : VS 15 | BITS: 32 16 | - platform: x64 17 | BUILDER : VS 18 | BITS: 64 19 | 20 | configuration: Debug 21 | shallow_clone: true 22 | clone_depth: 10 23 | init: 24 | - set MAKEFLAGS=-s 25 | - set MSYS2_PATH=c:\msys64 26 | - set CHERE_INVOKING=1 27 | - if "%BUILDER%_%PLATFORM%"=="MSYS2_x86" set MSYSTEM=MINGW32 28 | - if "%BUILDER%_%PLATFORM%"=="MSYS2_x64" set MSYSTEM=MINGW64 29 | - if "%BUILDER%"=="VS" set PATH=C:\Program Files (x86)\MSBuild\14.0\Bin;%PATH% 30 | - cd %APPVEYOR_BUILD_FOLDER%\..\ 31 | - set OF_ROOT=%CD%\openFrameworks 32 | - set OF_ADDON_NAME=%APPVEYOR_PROJECT_NAME% 33 | install: 34 | - git clone --depth=1 --branch=CI-TESTS https://github.com/bakercp/openFrameworks %OF_ROOT% 35 | - cd %OF_ROOT% 36 | - call scripts\ci\addons\install.cmd 37 | build_script: 38 | - cd %OF_ROOT% 39 | - call scripts\ci\addons\build.cmd 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # A .gitignore for openFrameworks addons. 2 | 3 | ################################################################################ 4 | # Documentation 5 | ################################################################################ 6 | 7 | # Doxygen-Related 8 | docs/html/* 9 | docs/* 10 | !docs/Doxyfile 11 | 12 | # Instructions and other documentation files. 13 | !docs/*.md 14 | !docs/*.txt 15 | 16 | ################################################################################ 17 | # Examples 18 | ################################################################################ 19 | 20 | # bin/data Folder 21 | example**/bin/* 22 | example**/bin/data/* 23 | !example**/bin/data 24 | !example**/bin/data/data.txt 25 | 26 | 27 | # Project files 28 | example**/*.xcodeproj 29 | example**/*.xcconfig 30 | example**/config.make 31 | example**/Makefile 32 | example**/*.plist 33 | !example**/addons.make 34 | 35 | 36 | ################################################################################ 37 | # Libraries 38 | ################################################################################ 39 | 40 | # Exclude 3rd party libs by default. 41 | libs/* 42 | 43 | # Don't exclude ofx libs by default. 44 | !libs/ofx* 45 | 46 | ################################################################################ 47 | # Shared Data 48 | ################################################################################ 49 | 50 | shared/data/* 51 | !shared/data/*.sh 52 | 53 | ################################################################################ 54 | # Scripts 55 | ################################################################################ 56 | 57 | # Everything included by default. 58 | 59 | ################################################################################ 60 | # Build Artifacts 61 | ################################################################################ 62 | 63 | # Default Qt Creator Build Folders 64 | build-example*/ 65 | 66 | # Xcodebuild build folders 67 | build/ 68 | 69 | ################################################################################ 70 | # Utility Stuff 71 | ################################################################################ 72 | 73 | # Local folders, use for temporary storage, etc. 74 | __*/* 75 | 76 | # Temporary files. 77 | *.autosave 78 | 79 | # OSX 80 | .DS_Store 81 | .AppleDouble 82 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "scripts/ofx"] 2 | path = scripts/ofx 3 | url = https://github.com/bakercp/ofx.git 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c++ 2 | compiler: gcc 3 | sudo: true 4 | matrix: 5 | include: 6 | # fully specify builds, include can't dynamically expand matrix entries 7 | # relative order of sudo and env is important so that addons: is recognized 8 | 9 | # Linux 64bit, OF master 10 | - os: linux 11 | dist: trusty 12 | sudo: required 13 | env: TARGET="linux64" OF_BRANCH="master" 14 | addons: 15 | apt: 16 | sources: 17 | - ubuntu-toolchain-r-test 18 | packages: 19 | - doxygen 20 | - realpath 21 | - gcc-4.9 22 | - g++-4.9 23 | - gdb 24 | 25 | # OSX, OF master 26 | - os: osx 27 | # osx_image: xcode10.1 28 | compiler: clang 29 | env: TARGET="osx" OF_BRANCH="master" 30 | addons: 31 | homebrew: 32 | packages: 33 | - doxygen 34 | update: true 35 | 36 | # before_install: 37 | install: 38 | - git clone --depth=1 --branch=master https://github.com/openFrameworks/openFrameworks.git ~/openFrameworks 39 | - /usr/bin/env bash ~/openFrameworks/scripts/ci/addons/install.sh 40 | - git clone https://github.com/bakercp/ofx.git ~/openFrameworks/scripts/ofx 41 | script: 42 | - /usr/bin/env bash ~/openFrameworks/scripts/ofx/ofx ci script ${TRAVIS_REPO_SLUG#*/} 43 | # after_script: 44 | # after_success: 45 | # after_failure: 46 | 47 | git: 48 | depth: 1 49 | quiet: true 50 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Christopher Baker 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ofxMaps 2 | ============ 3 | 4 | ## Description 5 | 6 | A slippy map addon inspired by https://github.com/RandomEtc/modestmaps-of and http://unfoldingmaps.org/. 7 | 8 | ## Features 9 | 10 | See examples. 11 | 12 | ## Getting Started 13 | 14 | To get started, generate the example project files using the openFrameworks [Project Generator](http://openframeworks.cc/learning/01_basics/how_to_add_addon_to_project/). 15 | 16 | ## Documentation 17 | 18 | API documentation can be found here. 19 | 20 | ## Build Status 21 | 22 | Linux, macOS [![Build Status](https://travis-ci.org/bakercp/ofxMaps.svg?branch=master)](https://travis-ci.org/bakercp/ofxMaps) 23 | 24 | Visual Studio, MSYS [![Build status](https://ci.appveyor.com/api/projects/status/4w3ix4fm9le5lwqd/branch/master?svg=true)](https://ci.appveyor.com/project/bakercp/ofxmaps/branch/master) 25 | 26 | ## Compatibility 27 | 28 | The `stable` branch of this repository is meant to be compatible with the openFrameworks [stable branch](https://github.com/openframeworks/openFrameworks/tree/stable), which corresponds to the latest official openFrameworks release. 29 | 30 | The `master` branch of this repository is meant to be compatible with the openFrameworks [master branch](https://github.com/openframeworks/openFrameworks/tree/master). 31 | 32 | Some past openFrameworks releases are supported via tagged versions, but only `stable` and `master` branches are actively supported. 33 | 34 | ## Versioning 35 | 36 | This project uses Semantic Versioning, although strict adherence will only come into effect at version 1.0.0. 37 | 38 | ## Licensing 39 | 40 | See `LICENSE.md`. 41 | 42 | ## Contributing 43 | 44 | Pull Requests are always welcome, so if you make any improvements please feel free to float them back upstream :) 45 | 46 | 1. Fork this repository. 47 | 2. Create your feature branch (`git checkout -b my-new-feature`). 48 | 3. Commit your changes (`git commit -am 'Add some feature'`). 49 | 4. Push to the branch (`git push origin my-new-feature`). 50 | 5. Create new Pull Request. 51 | -------------------------------------------------------------------------------- /addon_config.mk: -------------------------------------------------------------------------------- 1 | meta: 2 | ADDON_NAME = ofxMaps 3 | ADDON_DESCRIPTION = A tile-based mapping addon. 4 | ADDON_AUTHOR = bakercp 5 | ADDON_TAGS = "web" "client" "server" "map" "gis" 6 | ADDON_URL = http://github.com/bakercp/ofxMaps 7 | 8 | common: 9 | ADDON_DEPENDENCIES = ofxHTTP ofxGeo ofxCache ofxSQLiteCpp ofxSpatialHash ofxTaskQueue 10 | -------------------------------------------------------------------------------- /example/addons.make: -------------------------------------------------------------------------------- 1 | ofxCache 2 | ofxGeo 3 | ofxHTTP 4 | ofxIO 5 | ofxMaps 6 | ofxMediaType 7 | ofxNetworkUtils 8 | ofxPoco 9 | ofxSQLiteCpp 10 | ofxSSLManager 11 | ofxSpatialHash 12 | ofxTaskQueue 13 | -------------------------------------------------------------------------------- /example/bin/data/provider.json: -------------------------------------------------------------------------------- 1 | { 2 | "tilejson": "1.0.0", 3 | "name": "OpenStreetMap", 4 | "description": "A free editable map of the whole world.", 5 | "version": "1.0.0", 6 | "attribution": "(c) OpenStreetMap contributors, CC-BY-SA", 7 | "scheme": "xyz", 8 | "tiles": [ 9 | "https://a.tile.openstreetmap.org/{z}/{x}/{y}.png", 10 | "https://b.tile.openstreetmap.org/{z}/{x}/{y}.png", 11 | "https://c.tile.openstreetmap.org/{z}/{x}/{y}.png" 12 | ], 13 | "minzoom": 0, 14 | "maxzoom": 18, 15 | "bounds": [ -180, -85, 180, 85 ] 16 | } -------------------------------------------------------------------------------- /example/src/main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2014 Christopher Baker 3 | // 4 | // SPDX-License-Identifier: MIT 5 | // 6 | 7 | 8 | #include "ofAppRunner.h" 9 | #include "ofApp.h" 10 | 11 | 12 | int main() 13 | { 14 | ofGLWindowSettings settings; 15 | settings.setSize(1280, 768); 16 | settings.setGLVersion(3, 2); 17 | settings.windowMode = OF_FULLSCREEN; 18 | auto window = ofCreateWindow(settings); 19 | auto app = std::make_shared(); 20 | return ofRunApp(app); 21 | } 22 | -------------------------------------------------------------------------------- /example/src/ofApp.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2014 Christopher Baker 3 | // 4 | // SPDX-License-Identifier: MIT 5 | // 6 | 7 | 8 | #include "ofApp.h" 9 | 10 | 11 | void ofApp::setup() 12 | { 13 | coordinates = 14 | { 15 | { 42.2610492, -91.4501953 }, 16 | { 43.0046471, -90.4833984 }, 17 | { 43.0367759, -89.3847656 }, 18 | { 41.9676592, -88.4619141 }, 19 | { 41.2117215, -89.0332031 }, 20 | { 40.5805847, -90.1318359 }, 21 | { 40.6806380, -91.1865234 }, 22 | { 41.1124688, -92.4169922 }, 23 | { 42.1959688, -93.2958984 }, 24 | { 43.2932003, -92.1972656 }, 25 | { 44.0560117, -90.7470703 } 26 | }; 27 | 28 | ofJson json = ofLoadJson("provider.json"); 29 | 30 | tileProvider = std::make_shared(ofxMaps::MapTileProvider::fromJSON(json)); 31 | Poco::ThreadPool::defaultPool().addCapacity(64); 32 | bufferCache = std::make_shared(*tileProvider, "cache/"); 33 | tileSet = std::make_shared(1024, 34 | tileProvider, 35 | bufferCache); 36 | 37 | tileLayer = std::make_shared(tileSet, 1 * 1920, 1 * 1080); 38 | 39 | ofxGeo::Coordinate chicago(41.8827, -87.6233); 40 | ofxGeo::Coordinate bethel(45.0579, -93.1605); 41 | 42 | tileLayer->setCenter(coordinates[3], 21); 43 | 44 | } 45 | 46 | 47 | void ofApp::update() 48 | { 49 | tileLayer->update(); 50 | 51 | if (!ofIsFloatEqual(animation, 0.f)) 52 | tileLayer->setCenter(tileLayer->getCenter().getNeighbor(animation, 0)); 53 | } 54 | 55 | 56 | void ofApp::draw() 57 | { 58 | // ofScale(0.25, 0.25, 1); 59 | // ofBackgroundGradient(ofColor(255), ofColor(0)); 60 | // ofFill(); 61 | // ofSetColor(255); 62 | 63 | // cam.begin(); 64 | ofPushMatrix(); 65 | //ofTranslate(-tileLayer->getWidth() / 2, -tileLayer->getHeight() / 2); 66 | tileLayer->draw(0, 0); 67 | ofPopMatrix(); 68 | 69 | ofPushStyle(); 70 | ofNoFill(); 71 | ofSetColor(0, 255, 0); 72 | 73 | for (auto coordinate: coordinates) 74 | { 75 | auto tc = tileLayer->geoToPixels(coordinate); 76 | ofDrawCircle(tc.x, tc.y, 20); 77 | } 78 | ofPopStyle(); 79 | 80 | // cam.end(); 81 | 82 | ofDrawBitmapStringHighlight(tileLayer->getCenter().toString(0), 14, ofGetHeight() - 32); 83 | ofDrawBitmapStringHighlight("Task Queue:" + ofx::TaskQueue::instance().toString(), 14, ofGetHeight() - 16); 84 | ofDrawBitmapStringHighlight("Connection Pool: " + bufferCache->toString(), 14, ofGetHeight() - 2); 85 | 86 | } 87 | 88 | 89 | void ofApp::keyPressed(int key) 90 | { 91 | if (key == 'f' || key == 'F') 92 | { 93 | ofToggleFullscreen(); 94 | } 95 | else if (key == '-') 96 | { 97 | tileLayer->setCenter(tileLayer->getCenter().getZoomedBy(-1)); 98 | } 99 | else if (key == '=') 100 | { 101 | tileLayer->setCenter(tileLayer->getCenter().getZoomedBy(1)); 102 | } 103 | else if (key == 'w') 104 | { 105 | tileLayer->setCenter(tileLayer->getCenter().getNeighborUp()); 106 | } 107 | else if (key == 'a') 108 | { 109 | tileLayer->setCenter(tileLayer->getCenter().getNeighborLeft()); 110 | } 111 | else if (key == 's') 112 | { 113 | tileLayer->setCenter(tileLayer->getCenter().getNeighborDown()); 114 | } 115 | else if (key == 'd') 116 | { 117 | tileLayer->setCenter(tileLayer->getCenter().getNeighborRight()); 118 | } 119 | else if (key == '1') 120 | { 121 | animation -= 0.01;; 122 | } 123 | else if (key == '2') 124 | { 125 | animation += 0.01;; 126 | } 127 | else if (key == '3') 128 | { 129 | animation = 0; 130 | } 131 | // else if (key == ' ') 132 | // { 133 | // setsIndex = (setsIndex + 1) % sets.size(); 134 | // tileLayer->setSetId(sets[setsIndex]); 135 | // } 136 | 137 | } 138 | 139 | -------------------------------------------------------------------------------- /example/src/ofApp.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2014 Christopher Baker 3 | // 4 | // SPDX-License-Identifier: MIT 5 | // 6 | 7 | 8 | #pragma once 9 | 10 | 11 | #include "ofMain.h" 12 | #include "ofxMaps.h" 13 | 14 | 15 | class ofApp: public ofBaseApp 16 | { 17 | public: 18 | void setup() override; 19 | void update() override; 20 | void draw() override; 21 | 22 | void keyPressed(int key) override; 23 | 24 | std::shared_ptr bufferCache; 25 | std::shared_ptr tileLayer; 26 | std::shared_ptr tileSet; 27 | std::shared_ptr tileProvider; 28 | 29 | std::vector coordinates; 30 | 31 | std::vector sets; 32 | int setsIndex = 0; 33 | 34 | float animation = 0; 35 | 36 | ofEasyCam cam; 37 | 38 | }; 39 | -------------------------------------------------------------------------------- /libs/ofxMaps/includes/ofx/Maps/AbstractMapTypes.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2014 Christopher Baker 3 | // 4 | // SPDX-License-Identifier: MIT 5 | // 6 | 7 | 8 | #pragma once 9 | 10 | 11 | #include 12 | #include "ofx/Geo/Coordinate.h" 13 | #include "ofx/Geo/CoordinateBounds.h" 14 | #include "ofx/Maps/Tile.h" 15 | 16 | 17 | namespace ofx { 18 | namespace Maps { 19 | 20 | 21 | class TileCoordinate; 22 | class TileKey; 23 | 24 | 25 | /// \brief An abstract class representing a Map Tile Provider. 26 | class AbstractTileProvider 27 | { 28 | public: 29 | /// \brief Destroy the AbstractMapTileProvider. 30 | virtual ~AbstractTileProvider() 31 | { 32 | } 33 | 34 | /// \brief Get the provider's unique ID. 35 | /// 36 | /// This unique provider id is primarily used for caching purposes. 37 | /// 38 | /// \returns a unique provider ID. 39 | virtual std::string id() const = 0; 40 | 41 | /// \brief Get the name of this provider if available. 42 | /// \returns the name of this provider. 43 | virtual std::string name() const = 0; 44 | 45 | /// \brief Get the description of this provider if available. 46 | /// \returns the description of this provider. 47 | virtual std::string description() const = 0; 48 | 49 | /// \brief Get the attribution string for the current tile provider. 50 | /// \returns An attribution string. 51 | virtual std::string attribution() const = 0; 52 | 53 | /// \brief Get the version of this provider data if available. 54 | /// \returns the version of this provider data. 55 | virtual std::string version() const = 0; 56 | 57 | /// \brief Get the minimum zoom level for this provider. 58 | /// \returns the minimum zoom level. 59 | virtual int minZoom() const = 0; 60 | 61 | /// \brief Get the maximum zoom level for this provider. 62 | /// \returns the maximum zoom level. 63 | virtual int maxZoom() const = 0; 64 | 65 | /// \brief Get the tile width. 66 | /// \returns the tile width; 67 | virtual float tileWidth() const = 0; 68 | 69 | /// \brief Get the tile height; 70 | /// \returns the tile height; 71 | virtual float tileHeight() const = 0; 72 | 73 | /// \brief Get the tile size. 74 | /// \returns the tile size. 75 | virtual glm::vec2 tileSize() const = 0; 76 | 77 | /// \brief Get the zoom level for a given map scale. 78 | /// \param scale The scale to calculate. 79 | /// \returns the zoom level for the given scale. 80 | virtual double zoomForScale(double scale) const = 0; 81 | 82 | /// \brief Determine if this provider is cacheable. 83 | /// 84 | /// Some providers, such as local databases are write only and should not 85 | /// be cached. 86 | /// 87 | /// \returns true if this provider is cachable. 88 | virtual bool isCacheable() const = 0; 89 | 90 | /// \brief Get a URI for the given tile coordinate. 91 | /// \param coordinate The TileCoordinate to get a URI for. 92 | /// \returns the URI for the given TileCoordinate. 93 | virtual std::string getTileURI(const TileKey& key) const = 0; 94 | 95 | }; 96 | 97 | 98 | class AbstractMapTileProvider: public AbstractTileProvider 99 | { 100 | public: 101 | /// \brief Destroy the AbstractMapTileProvider. 102 | virtual ~AbstractMapTileProvider() 103 | { 104 | } 105 | 106 | /// \returns the bounds for this provider. 107 | virtual Geo::CoordinateBounds bounds() const = 0; 108 | 109 | /// \returns the initial center for this provider. 110 | virtual TileCoordinate center() const = 0; 111 | 112 | /// \brief Get the TileCoordinate from the given Geo::Coordinate at the default zoom. 113 | /// \param location The the Geo::Coordinate at the default zoom level. 114 | /// \returns the TileCoordinate at the default zoom level. 115 | virtual TileCoordinate geoToWorld(const Geo::Coordinate& location) const = 0; 116 | 117 | /// \brief Get the GeoCoordinate from the given TileCoordinate. 118 | /// \param coordinate The TileCoordinate to transform to a Geo::Coordinate. 119 | /// \returns the GeoCoordinate corresponding to the TileCoordinate. 120 | virtual Geo::Coordinate tileToGeo(const TileCoordinate& coordinate) const = 0; 121 | 122 | }; 123 | 124 | 125 | } } // namespace ofx::Maps 126 | -------------------------------------------------------------------------------- /libs/ofxMaps/includes/ofx/Maps/BaseProjection.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2014 Christopher Baker 3 | // Copyright (c) -2014 Tom Carden 4 | // 5 | // SPDX-License-Identifier: MIT 6 | // 7 | 8 | 9 | #pragma once 10 | 11 | 12 | #include "ofConstants.h" 13 | #include "ofx/Geo/Coordinate.h" 14 | 15 | 16 | namespace ofx { 17 | namespace Maps { 18 | 19 | 20 | class TileCoordinate; 21 | 22 | 23 | /// \brief A 2D linear transformation. 24 | class Transformation 25 | { 26 | public: 27 | /// \brief Create an identity Transformation. 28 | Transformation(); 29 | 30 | /// \brief Create a Transform with a, b and c coefficients. 31 | Transformation(double ax, double bx, double cx, 32 | double ay, double by, double cy); 33 | 34 | /// \brief Generates a transform based on three pairs of points. 35 | /// 36 | /// a1 -> a2, b1 -> b2, c1 -> c2. 37 | /// 38 | /// \returns the derived Transformation. 39 | Transformation(double a1x, double a1y, 40 | double a2x, double a2y, 41 | double b1x, double b1y, 42 | double b2x, double b2y, 43 | double c1x, double c1y, 44 | double c2x, double c2y); 45 | 46 | glm::dvec2 transform(const glm::dvec2& point) const; 47 | 48 | glm::dvec2 untransform(const glm::dvec2& point) const; 49 | 50 | /// \brief Solves a system of linear equations. 51 | /// 52 | /// t1 = (x * r1) + (y + s1) + z 53 | /// t2 = (x * r2) + (y + s2) + z 54 | /// t3 = (x * r3) + (y + s3) + z 55 | /// 56 | /// r1 - t3 are the known values. 57 | /// x, y, z are the unknowns to be solved. 58 | /// 59 | /// \returns a vector of a, b, c values. 60 | static glm::dvec3 linearSolution(double r1, double s1, double t1, 61 | double r2, double s2, double t2, 62 | double r3, double s3, double t3); 63 | 64 | double ax = 1.0; 65 | double bx = 0.0; 66 | double cx = 0.0; 67 | double ay = 0.0; 68 | double by = 1.0; 69 | double cy = 0.0; 70 | 71 | }; 72 | 73 | 74 | /// \brief A class responsible for projecting coordinates. 75 | class BaseProjection 76 | { 77 | public: 78 | /// \brief Construct a BaseProjection with a default zoom and Transformation. 79 | /// \param zoom The default zoom level used by this Projection. 80 | /// \param transformation The transformation used by this projection. 81 | BaseProjection(const std::string& name, 82 | double zoom, 83 | const Transformation& transformation); 84 | 85 | /// \brief Destroy the BaseProjection. 86 | virtual ~BaseProjection(); 87 | 88 | /// \returns the name of this projection. 89 | std::string name() const; 90 | 91 | /// \returns the default zoom level used with this projection. 92 | double zoom() const; 93 | 94 | /// \brief Get the TileCoordinate from the given Geo::Coordinate at the default zoom. 95 | /// 96 | /// World corodinates are tile coordinates at the default zoom (typically 0). 97 | /// 98 | /// \param location The the Geo::Coordinate at the default zoom level. 99 | /// \returns the TileCoordinate at the default zoom level. 100 | TileCoordinate geoToWorld(const Geo::Coordinate& location) const; 101 | 102 | /// \brief Get the GeoCoordinate from the given TileCoordinate. 103 | /// \param coordinate The TileCoordinate to transform to a Geo::Coordinate. 104 | /// \returns the GeoCoordinate corresponding to the TileCoordinate. 105 | Geo::Coordinate tileToGeo(const TileCoordinate& coordinate) const; 106 | 107 | protected: 108 | /// \brief Calculate the raw projection between two points. 109 | /// \param point The point to be projected. 110 | /// \returns The projected point. 111 | virtual glm::dvec2 rawProject(const glm::dvec2& unProjectedPoint) const = 0; 112 | 113 | /// \brief Calculate the raw reverse projection between two points. 114 | /// \param point The point to be unprojected. 115 | /// \returns The unprojected point. 116 | virtual glm::dvec2 rawUnproject(const glm::dvec2& projectedPoint) const = 0; 117 | 118 | glm::dvec2 project(const glm::dvec2& point) const; 119 | glm::dvec2 unproject(const glm::dvec2& point) const; 120 | 121 | /// \brief The default zoom level used by this Projection. 122 | double _zoom; 123 | 124 | /// \brief The transformation used by this projection. 125 | Transformation _transformation; 126 | 127 | /// \brief The name of the proejction. 128 | std::string _name; 129 | }; 130 | 131 | 132 | } } // namespace ofx::Maps 133 | -------------------------------------------------------------------------------- /libs/ofxMaps/includes/ofx/Maps/MBTilesCache.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2014 Christopher Baker 3 | // 4 | // SPDX-License-Identifier: MIT 5 | // 6 | 7 | 8 | #pragma once 9 | 10 | 11 | #include "Poco/Net/NameValueCollection.h" 12 | #include "SQLiteCpp.h" 13 | #include "SQLiteConnection.h" 14 | #include "SQLiteConnectionPool.h" 15 | #include "ofThreadChannel.h" 16 | #include "ofx/IO/ThreadChannel.h" 17 | #include "ofx/Cache/BaseCache.h" 18 | #include "ofx/Geo/CoordinateBounds.h" 19 | #include "ofx/Geo/Coordinate.h" 20 | #include "ofx/Maps/Tile.h" 21 | #include "ofx/Maps/TileKey.h" 22 | #include "ofx/Maps/TileCoordinate.h" 23 | #include "ofx/Maps/MapTileProvider.h" 24 | 25 | 26 | namespace ofx { 27 | namespace Maps { 28 | 29 | 30 | class MBTilesMetadata: public Poco::Net::NameValueCollection 31 | { 32 | public: 33 | MBTilesMetadata(); 34 | 35 | virtual ~MBTilesMetadata(); 36 | 37 | /// \returns the minimum zoom level. 38 | int minZoom() const; 39 | 40 | /// \returns the maximum zoom level. 41 | int maxZoom() const; 42 | 43 | /// \returns the bounding box of the tiles. 44 | Geo::CoordinateBounds bounds() const; 45 | 46 | /// \returns the center of the tiles. 47 | TileCoordinate center() const; 48 | 49 | /// \returns the plain-english name of the tileset. 50 | std::string attribution() const; 51 | 52 | /// \returns the plain-english name of the tileset. 53 | std::string name() const; 54 | 55 | /// \returns the data type, overlay or baselayer. 56 | std::string type() const; 57 | 58 | /// \returns the version of the tileset, as a plain number. 59 | std::string version() const; 60 | 61 | /// \returns a description of the layer as plain text. 62 | std::string description(); 63 | 64 | /// \returns the image file format of the data. 65 | std::string format() const; 66 | 67 | static const std::string KEY_MIN_ZOOM; 68 | static const std::string KEY_MAX_ZOOM; 69 | static const std::string KEY_BOUNDS; 70 | static const std::string KEY_CENTER; 71 | static const std::string KEY_NAME; 72 | static const std::string KEY_TYPE; 73 | static const std::string KEY_VERSION; 74 | static const std::string KEY_DESCRIPTION; 75 | static const std::string KEY_ATTRIBUTION; 76 | static const std::string KEY_FORMAT; 77 | 78 | }; 79 | 80 | 81 | class MBTilesConnection: public SQLite::SQLiteConnection 82 | { 83 | public: 84 | using SQLite::SQLiteConnection::SQLiteConnection; 85 | 86 | // MBTilesConnection(const std::string& filename, 87 | // Mode mode = Mode::READ_ONLY, 88 | // uint64_t databaseTimeoutMilliseconds = 0, 89 | // Poco::RWLock* mutex = nullptr); 90 | 91 | /// \brief Destroy the MBTiles. 92 | virtual ~MBTilesConnection(); 93 | 94 | MBTilesMetadata getMetaData() const noexcept; 95 | 96 | bool setMetaData(const MBTilesMetadata& metadata) noexcept; 97 | 98 | bool has(const TileKey& key) const noexcept; 99 | 100 | std::shared_ptr getBuffer(const TileKey& key) const noexcept; 101 | 102 | std::shared_ptr getTile(const TileKey& key) const noexcept; 103 | 104 | bool setTile(const TileKey& key, 105 | const ofBuffer& image) noexcept; 106 | 107 | std::size_t size() const noexcept; 108 | 109 | static const std::string QUERY_SELECT_METADATA; 110 | static const std::string QUERY_INSERT_METADATA; 111 | static const std::string CREATE_TABLE_METADATA; 112 | static const std::string DROP_TABLE_METADATA; 113 | 114 | static const std::string QUERY_TILES; 115 | static const std::string QUERY_TILES_WITH_SET_ID; 116 | static const std::string COUNT_TILES; 117 | static const std::string COUNT_TILES_WITH_SET_ID; 118 | 119 | static const std::string INSERT_IMAGE; 120 | static const std::string COUNT_IMAGE; 121 | 122 | static const std::string INSERT_MAP; 123 | static const std::string INSERT_MAP_WITH_SET_ID; 124 | static const std::string COUNT_MAP; 125 | static const std::string COUNT_MAP_WITH_SET_ID; 126 | 127 | static const std::string COUNT_ALL; 128 | 129 | static const std::string MBTILES_SCHEMA; 130 | 131 | }; 132 | 133 | 134 | class MBTilesCache: public Cache::BaseCache 135 | { 136 | public: 137 | typedef SQLite::SQLiteConnectionPool_ MBTilesConnectionPool; 138 | 139 | MBTilesCache(const MapTileProvider& tileProvider, 140 | const std::string& cachePath, 141 | uint64_t databaseTimeoutMilliseconds = 5000, 142 | std::size_t capacity = MBTilesConnectionPool::DEFAULT_CAPACITY, 143 | std::size_t peakCapacity = MBTilesConnectionPool::DEFAULT_PEAK_CAPACITY); 144 | 145 | virtual ~MBTilesCache(); 146 | 147 | const MBTilesConnectionPool& readConnectionPool() const; 148 | 149 | std::string path() const 150 | { 151 | return _writeConnection->database().getFilename(); 152 | } 153 | 154 | std::string to_string() const 155 | { 156 | return _readConnectionPool->toString() + " Writer: ";// std::to_string(_writeChannel.size()); 157 | } 158 | 159 | std::string toString() const 160 | { 161 | return to_string(); 162 | } 163 | 164 | protected: 165 | bool doHas(const TileKey& key) const override; 166 | 167 | std::shared_ptr doGet(const TileKey& key) override; 168 | 169 | void doAdd(const TileKey& key, std::shared_ptr entry) override; 170 | 171 | void doRemove(const TileKey& key) override; 172 | 173 | std::size_t doSize() override; 174 | 175 | void doClear() override; 176 | 177 | private: 178 | std::thread _writeThread; 179 | 180 | ofThreadChannel>> _writeChannel; 181 | 182 | std::unique_ptr _writeConnection = nullptr; 183 | 184 | mutable std::unique_ptr _readConnectionPool = nullptr; 185 | 186 | }; 187 | 188 | 189 | //class MBTilesProvider 190 | 191 | 192 | } } // namespace ofx::Maps 193 | -------------------------------------------------------------------------------- /libs/ofxMaps/includes/ofx/Maps/MapNavigator.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2014 Christopher Baker 3 | // 4 | // SPDX-License-Identifier: MIT 5 | // 6 | 7 | 8 | #pragma once 9 | 10 | 11 | #include "ofVec2d.h" 12 | #include "ofEvents.h" 13 | #include "ofUtils.h" 14 | 15 | 16 | 17 | 18 | namespace ofx { 19 | namespace Maps { 20 | 21 | 22 | class MapNavigator 23 | { 24 | public: 25 | 26 | // void keyPressed(ofKeyEventArgs& evt); 27 | // void keyReleased(ofKeyEventArgs& evt); 28 | // 29 | // void mouseMoved(ofMouseEventArgs& evt); 30 | // void mouseDragged(ofMouseEventArgs& evt); 31 | // void mousePressed(ofMouseEventArgs& evt); 32 | // void mouseReleased(ofMouseEventArgs& evt); 33 | // 34 | // void windowEntered(ofEntryEventArgs& evt) 35 | // { 36 | // } 37 | // 38 | // double getZoom() const; 39 | // void setZoom(double zoom); 40 | // 41 | // void zoom(double zoomStep); 42 | // void zoomIn(); 43 | // void zoomOut(); 44 | // 45 | // void panBy(const ofVec2d& delta); 46 | // void panBy(double dx, double dy); 47 | // 48 | // void scaleBy(double s); 49 | // 50 | // void scaleBy(double s, const ofVec2d& c); 51 | // void scaleBy(double s, double cx, double cy); 52 | // void rotateBy(double r, double cx, double cy); 53 | // 54 | // Geo::Coordinate getGeoLocationCenter() const; 55 | // TileCoordinate getTileCoordinateCenter() const; 56 | // 57 | // void setPointCenter(const ofVec2d& center); 58 | // void setGeoLocationCenter(const Geo::Coordinate& location); 59 | // void setTileCoordinateCenter(const TileCoordinate& center); 60 | // 61 | // Geo::Coordinate pointToGeolocation(const ofVec2d& point) const; 62 | // TileCoordinate pointToTileCoordinate(const ofVec2d& point) const; 63 | // 64 | // ofVec2d geoLocationToPoint(const Geo::Coordinate& location) const; 65 | // ofVec2d tileCoordinateToPoint(const TileCoordinate& coord) const; 66 | // 67 | // 68 | // enum 69 | // { 70 | // DEFAULT_DOUBLE_CLICK_TIME = 250 71 | // }; 72 | // 73 | // double _rotation; 74 | 75 | }; 76 | 77 | 78 | } } // namespace ofx::Maps 79 | -------------------------------------------------------------------------------- /libs/ofxMaps/includes/ofx/Maps/MapTileLayer.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2014 Christopher Baker 3 | // 4 | // SPDX-License-Identifier: MIT 5 | // 6 | 7 | 8 | #pragma once 9 | 10 | 11 | #include 12 | #include 13 | #include "ofBaseTypes.h" 14 | #include "ofFbo.h" 15 | #include "ofMath.h" 16 | #include "ofTypes.h" 17 | #include "ofx/Maps/TileCoordinate.h" 18 | #include "ofx/Maps/TileKey.h" 19 | #include "ofx/Maps/MapTileSet.h" 20 | #include "ofMath.h" 21 | #include 22 | namespace ofx { 23 | namespace Maps { 24 | 25 | 26 | 27 | class QueueSorter 28 | { 29 | public: 30 | QueueSorter(const TileCoordinate& center): _center(center) 31 | { 32 | } 33 | 34 | // TODO: simplify these calculations by using internal vec calculations 35 | bool operator () (const TileCoordinate& c0, const TileCoordinate& c1) const 36 | { 37 | if (ofIsFloatEqual(c0.getZoom(), _center.getZoom())) 38 | { 39 | if (ofIsFloatEqual(c1.getZoom(), _center.getZoom())) 40 | { 41 | glm::dvec2 offset(0.5, 0.5); 42 | glm::dvec2 center2d(_center.getColumn(), _center.getRow()); 43 | glm::dvec2 c02d(c0.getColumn(), c0.getRow()); 44 | glm::dvec2 c12d(c1.getColumn(), c1.getRow()); 45 | double d0 = glm::distance2(center2d, c02d + offset); 46 | double d1 = glm::distance2(center2d, c12d + offset); 47 | return d0 < d1; 48 | } 49 | } 50 | else if (ofIsFloatEqual(c1.getZoom(), _center.getZoom())) 51 | { 52 | return false; 53 | } 54 | else 55 | { 56 | double d0 = std::fabs(c0.getZoom() - _center.getZoom()); 57 | double d1 = std::fabs(c1.getZoom() - _center.getZoom()); 58 | return d0 < d1; 59 | } 60 | 61 | return false; 62 | } 63 | 64 | protected: 65 | TileCoordinate _center; 66 | 67 | }; 68 | 69 | 70 | 71 | /// \brief A tile layer is a surface on which tiles are rendered. 72 | /// 73 | /// A tile layer has a tile provider, a width and a height. 74 | class MapTileLayer: public ofBaseDraws 75 | { 76 | public: 77 | MapTileLayer(std::shared_ptr store, float width, float height); 78 | 79 | virtual ~MapTileLayer(); 80 | 81 | void update(); 82 | 83 | void draw(float x, float y) const override; 84 | 85 | void draw(float x, float y, float w, float h) const override; 86 | 87 | glm::vec2 getSize() const; 88 | 89 | void setSize(const glm::vec2& size); 90 | 91 | float getWidth() const override; 92 | 93 | void setWidth(float width); 94 | 95 | float getHeight() const override; 96 | 97 | void setHeight(float height); 98 | 99 | const TileCoordinate& getCenter() const; 100 | 101 | void setCenter(const TileCoordinate& center); 102 | 103 | void setCenter(const Geo::Coordinate& center, double zoom); 104 | 105 | void setSetId(const std::string& setId); 106 | 107 | std::string getSetId() const; 108 | 109 | TileCoordinate pixelsToTile(const glm::vec2& pixelCoordinate) const; 110 | glm::vec2 tileToPixels(const TileCoordinate& tileCoordinate) const; 111 | 112 | Geo::Coordinate pixelsToGeo(const glm::vec2& pixelCoordinate) const; 113 | glm::vec2 geoToPixels(const Geo::Coordinate& geoCoordinate) const; 114 | 115 | // Geo::CoordinateBounds visibleBounds() const; 116 | 117 | protected: 118 | // virtual void drawMissing(const TileCoordinate& coordinate) const; 119 | 120 | TileKey keyForCoordinate(const TileCoordinate& coordinate) const; 121 | 122 | void cancelQueuedRequests() const; 123 | 124 | bool hasTile(const TileCoordinate& coordinate) const; 125 | 126 | std::shared_ptr getTile(const TileCoordinate& coordinate) const; 127 | 128 | void requestTiles(const std::set& coordinates) const; 129 | 130 | virtual std::set calculateVisibleCoordinates() const; 131 | 132 | /// \brief The tile store to render. 133 | std::shared_ptr _tiles; 134 | 135 | /// \brief The current set id being viewed. 136 | std::string _setId; 137 | 138 | /// \brief The size of the tile layer. 139 | glm::vec2 _size; 140 | 141 | /// \brief The padding of the layer. 142 | glm::ivec2 _padding; 143 | 144 | /// \brief Pan and anchor coordinate. 145 | TileCoordinate _center; 146 | 147 | void onTileCached(const std::pair>& args); 148 | void onTileUncached(const TileKey& args); 149 | void onTileRequestCancelled(const TileKey& key); 150 | void onTileRequestFailed(const Cache::RequestFailedArgs& args); 151 | 152 | /// \brief The current visible coordinates. 153 | mutable std::set _visisbleCoords; 154 | mutable std::set _outstandingRequests; 155 | 156 | std::unordered_map> _tilesToDraw; 157 | 158 | mutable bool _coordsDirty = true; 159 | mutable bool _fboNeedsResize = true; 160 | 161 | mutable ofFbo _fbo; 162 | 163 | ofEventListener _onTileCachedListener; 164 | ofEventListener _onTileUncachedListener; 165 | ofEventListener _onTileRequestCancelledListener; 166 | ofEventListener _onTileRequestFailedListener; 167 | 168 | }; 169 | 170 | 171 | } } // namespace ofx::Maps 172 | -------------------------------------------------------------------------------- /libs/ofxMaps/includes/ofx/Maps/MapTileProvider.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2014 Christopher Baker 3 | // 4 | // SPDX-License-Identifier: MIT 5 | // 6 | 7 | 8 | #pragma once 9 | 10 | 11 | #include "Poco/RegularExpression.h" 12 | #include "ofJson.h" 13 | #include "ofx/Maps/AbstractMapTypes.h" 14 | #include "ofx/Maps/BaseProjection.h" 15 | #include "ofx/Maps/SphericalMercatorProjection.h" 16 | #include "ofx/Maps/TileCoordinate.h" 17 | 18 | 19 | namespace ofx { 20 | namespace Maps { 21 | 22 | 23 | class TileKey; 24 | 25 | 26 | /// \brief A MapTileProvider with default settings. 27 | class MapTileProvider: public AbstractMapTileProvider 28 | { 29 | public: 30 | /// \brief Create a default MapTileProvider with no endpoint. 31 | MapTileProvider(); 32 | 33 | /// \brief Create a MapTileProvider. 34 | /// \param URITemplates The collection of template URLs. 35 | /// \param minZoom The minimum zoom level supported by the provider. 36 | /// \param maxZoom The maximum zoom level supported by the provider. 37 | /// \param tileWidth The width of the provider's tiles in pixels. 38 | /// \param tileHeight The height of the provider's tiles in pixels. 39 | /// \param bounds The bounds of the provider. 40 | /// \param center The initial center for this provider. 41 | /// \param projection The projection used by the provider. 42 | MapTileProvider(const std::vector& URITemplates, 43 | int minZoom, 44 | int maxZoom, 45 | int tileWidth, 46 | int tileHeight, 47 | const Geo::CoordinateBounds& bounds, 48 | const TileCoordinate& center, 49 | const BaseProjection& projection); 50 | 51 | /// \brief Destroy the MapTileProvider. 52 | virtual ~MapTileProvider(); 53 | 54 | virtual std::string id() const override; 55 | std::string name() const override; 56 | std::string description() const override; 57 | std::string attribution() const override; 58 | std::string version() const override; 59 | int minZoom() const override; 60 | int maxZoom() const override; 61 | float tileWidth() const override; 62 | float tileHeight() const override; 63 | glm::vec2 tileSize() const override; 64 | Geo::CoordinateBounds bounds() const override; 65 | TileCoordinate center() const override; 66 | double zoomForScale(double scale) const override; 67 | TileCoordinate geoToWorld(const Geo::Coordinate& location) const override; 68 | Geo::Coordinate tileToGeo(const TileCoordinate& coordinate) const override; 69 | std::string getTileURI(const TileKey& coordinate) const override; 70 | bool isCacheable() const override; 71 | 72 | /// \returns the URI templates. 73 | const std::vector URITemplates() const; 74 | 75 | /// \returns a collection of name value-pairs used used by this provider. 76 | std::map dictionary() const; 77 | 78 | enum 79 | { 80 | /// \brief The minimum zoom level supported by most map tile providers. 81 | DEFAULT_MIN_ZOOM = 0, 82 | /// \brief The maximum zoom level supported by most map tile providers. 83 | DEFAULT_MAX_ZOOM = 22, 84 | /// \brief The default tile width supported by most map tile providers. 85 | DEFAULT_TILE_WIDTH = 256, 86 | /// \brief The default tile height supported by most map tile providers. 87 | DEFAULT_TILE_HEIGHT = 256 88 | }; 89 | 90 | /// \brief The default map bounds. 91 | static const Geo::CoordinateBounds DEFAULT_BOUNDS; 92 | 93 | /// \brief The default map center. 94 | static const TileCoordinate DEFAULT_CENTER; 95 | 96 | /// \brief The default projection used by most map tile providers. 97 | static const SperhicalMercatorProjection DEFAULT_PROJECTION; 98 | 99 | /// \brief Create a MapTileProvider from JSON. 100 | /// 101 | /// This parses the JSON in the TileJSON 2.1.0 format. Not all features 102 | /// are supported. 103 | /// 104 | /// \param json The json to parse. 105 | /// \returns a configured MapTileProvider. 106 | /// \sa https://github.com/mapbox/tilejson-spec/tree/master/2.1.0 107 | static MapTileProvider fromJSON(const ofJson& json); 108 | 109 | /// \brief Export the MapTileProvider as JSON. 110 | /// 111 | /// This exports the provider in a TileJSON 2.1.0 format. Not all features 112 | /// are supported. 113 | /// 114 | /// \param provider The provider to save. 115 | /// \returns the json.. 116 | /// \sa https://github.com/mapbox/tilejson-spec/tree/master/2.1.0 117 | static ofJson toJSON(const MapTileProvider& provider); 118 | 119 | protected: 120 | /// \brief Extracts a template parameter value if it is available. 121 | /// 122 | /// This class should be overriden if the provider uses additional URI 123 | /// template variables. 124 | /// 125 | /// \param coordinate The tile coordinate requested. 126 | /// \param templateParameter The template parameter requested. 127 | /// \param templateValue The extracted value to be filled. 128 | /// \returns true iff the extraction was successful. 129 | virtual bool getTileURITemplateValue(const TileKey& key, 130 | const std::string& templateParameter, 131 | std::string& templateValue) const; 132 | 133 | /// \brief The URI template used for extraction. 134 | std::vector _URITemplates; 135 | 136 | /// \brief A collection of URI template parameters for each of the templates. 137 | std::vector> _URITemplateParameters; 138 | 139 | private: 140 | void _setURITemplates(const std::vector& templates); 141 | 142 | /// \brief A unique ID for the provider, based on the URI template. 143 | std::string _id; 144 | 145 | /// \brief This provider's name. 146 | std::string _name; 147 | 148 | /// \brief This provider's description. 149 | std::string _description; 150 | 151 | /// \brief A string containing this provider's attribution. 152 | std::string _attribution; 153 | 154 | /// \brief A string containing this provider's data version. 155 | std::string _version; 156 | 157 | /// \brief The minimum zoom level for this provider. 158 | int _minZoom; 159 | 160 | /// \brief The maximum zoom level for this provider. 161 | int _maxZoom; 162 | 163 | /// \brief The tile width and height used by this provider. 164 | glm::ivec2 _tileSize; 165 | 166 | /// \brief The bounds for this provider. 167 | Geo::CoordinateBounds _bounds; 168 | 169 | /// \brief The initial center for this provider. 170 | TileCoordinate _center; 171 | 172 | /// \brief A reference to this provider's projection. 173 | const BaseProjection& _projection; 174 | 175 | /// \brief A dictionary of unknown parameters that can be used for template matching. 176 | std::map _dictionary; 177 | 178 | }; 179 | 180 | 181 | } } // namespace ofx::Maps 182 | -------------------------------------------------------------------------------- /libs/ofxMaps/includes/ofx/Maps/MapTileSet.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2014 Christopher Baker 3 | // 4 | // SPDX-License-Identifier: MIT 5 | // 6 | 7 | 8 | #pragma once 9 | 10 | 11 | #include "Poco/Task.h" 12 | #include "Poco/TaskNotification.h" 13 | #include "ofImage.h" 14 | #include "ofx/TaskQueue.h" 15 | #include "ofx/LRUCache.h" 16 | #include "ofx/Cache/BaseHTTPStore.h" 17 | #include "ofx/Cache/ResourceLoader.h" 18 | #include "ofx/Maps/AbstractMapTypes.h" 19 | #include "ofx/Maps/TileCoordinate.h" 20 | #include "ofx/Maps/MapTileProvider.h" 21 | #include "ofx/Maps/TileKey.h" 22 | #include "ofx/HTTP/ClientEvents.h" 23 | #include "ofx/HTTP/Client.h" 24 | #include "ofx/HTTP/ThreadSettings.h" 25 | 26 | 27 | namespace ofx { 28 | namespace Maps { 29 | 30 | 31 | 32 | class MapTileSet: public Cache::BaseResourceCache 33 | { 34 | public: 35 | typedef Cache::BaseCache TileBufferCache; 36 | 37 | MapTileSet(std::size_t cacheSize, 38 | std::shared_ptr provider, 39 | std::shared_ptr bufferCache = nullptr); 40 | 41 | std::shared_ptr load(Cache::CacheRequestTask& task) override; 42 | std::string toTaskId(const TileKey& key) const override; 43 | 44 | std::shared_ptr provider() const; 45 | 46 | static const std::string DEFAULT_BUFFER_CACHE_LOCATION; 47 | 48 | protected: 49 | std::shared_ptr _tryLoadFromCache(Cache::CacheRequestTask& task); 50 | std::shared_ptr _tryLoadFromURI(Cache::CacheRequestTask& task); 51 | 52 | void _onAdd(const std::pair>& args); 53 | 54 | std::shared_ptr _bufferCache; 55 | 56 | private: 57 | /// \brief The tile provider associated with this loader. 58 | std::shared_ptr _provider; 59 | 60 | /// \brief The onPut event listener used to load texture in the main thread. 61 | ofEventListener _onAddListener; 62 | 63 | // /// \brief The store mutex. 64 | // mutable std::mutex _mutex; 65 | }; 66 | 67 | 68 | } } // namespace ofx::Maps 69 | -------------------------------------------------------------------------------- /libs/ofxMaps/includes/ofx/Maps/SphericalMercatorProjection.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2014 Christopher Baker 3 | // Copyright (c) -2014 Tom Carden 4 | // 5 | // SPDX-License-Identifier: MIT 6 | // 7 | 8 | 9 | #pragma once 10 | 11 | 12 | #include "ofx/Maps/BaseProjection.h" 13 | 14 | 15 | namespace ofx { 16 | namespace Maps { 17 | 18 | 19 | /// \brief An implementation of the EPSG:3857 project used by most tiled maps. 20 | /// 21 | /// \sa http://gis.stackexchange.com/questions/48949/epsg-3857-or-4326-for-googlemaps-openstreetmap-and-leaflet 22 | /// \sa http://spatialreference.org/ref/sr-org/epsg3857/ 23 | class SperhicalMercatorProjection: public BaseProjection 24 | { 25 | public: 26 | /// \brief Create a default SperhicalMercatorProjection. 27 | /// \param name The name of the projection. 28 | /// \param zoom The default zoom used when calculating projections. 29 | SperhicalMercatorProjection(); 30 | 31 | /// \brief Destroy the SperhicalMercatorProjection. 32 | virtual ~SperhicalMercatorProjection(); 33 | 34 | enum 35 | { 36 | /// \brief The default zoom level used by this projection. 37 | DEFAULT_ZOOM = 0 38 | }; 39 | 40 | static const std::string EPSG_3857; 41 | 42 | protected: 43 | glm::dvec2 rawProject(const glm::dvec2& point) const; 44 | glm::dvec2 rawUnproject(const glm::dvec2& point) const; 45 | 46 | }; 47 | 48 | 49 | } } // namespace ofx::Maps 50 | -------------------------------------------------------------------------------- /libs/ofxMaps/includes/ofx/Maps/Tile.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2014 Christopher Baker 3 | // 4 | // SPDX-License-Identifier: MIT 5 | // 6 | 7 | 8 | #pragma once 9 | 10 | 11 | #include "ofPixels.h" 12 | #include "ofTexture.h" 13 | 14 | 15 | namespace ofx { 16 | namespace Maps { 17 | 18 | 19 | /// \brief A simple class representing an image tile. 20 | class Tile: public ofBaseDraws 21 | { 22 | public: 23 | enum class Type 24 | { 25 | /// \brief Indicates an empty tile, for instance returned if a zoom level is not available. 26 | EMPTY, 27 | /// \brief A standard ofPixels-based raster image. 28 | RASTER 29 | }; 30 | 31 | /// \brief Create an empty un-allocated tile. 32 | Tile(); 33 | 34 | /// \brief Create a tile with the given pixels. 35 | /// \param pixels The pixels to set. 36 | Tile(const ofPixels& pixels); 37 | 38 | /// \brief Destroy the Tile. 39 | virtual ~Tile(); 40 | 41 | using ofBaseDraws::draw; 42 | void draw(float x, float y, float width, float height) const override; 43 | float getWidth() const override; 44 | float getHeight() const override; 45 | 46 | /// \returns true if this is an empty un-allocated tile. 47 | bool empty() const; 48 | 49 | /// \returns a const reference to the pixels. 50 | const ofPixels& pixels() const; 51 | 52 | /// \returns a const reference to the texture. 53 | const ofTexture& texture() const; 54 | 55 | /// \returns the Tile type. 56 | Type type() const; 57 | 58 | /// \returns true if the texture is uploaded. 59 | bool hasTexture() const; 60 | 61 | /// \brief Upload the pixels to a texture if needed. 62 | void loadTexture(); 63 | 64 | /// \brief Clear the texture memory, but retain the pixels. 65 | void clearTexture(); 66 | 67 | private: 68 | /// \brief The Tile Type. 69 | Type _type = Type::EMPTY; 70 | 71 | /// \brief The pixels. 72 | ofPixels _pixels; 73 | 74 | /// \brief The texture. 75 | ofTexture _texture; 76 | 77 | }; 78 | 79 | 80 | } } // namespace ofx::Maps 81 | -------------------------------------------------------------------------------- /libs/ofxMaps/includes/ofx/Maps/TileCoordinate.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2014 Christopher Baker 3 | // Copyright (c) -2014 Tom Carden 4 | // 5 | // SPDX-License-Identifier: MIT 6 | // 7 | 8 | 9 | #pragma once 10 | 11 | 12 | #include 13 | #include 14 | #include "ofConstants.h" 15 | #include "ofFileUtils.h" 16 | 17 | 18 | namespace ofx { 19 | namespace Maps { 20 | 21 | 22 | 23 | 24 | /// \brief A tile coordinate in a tiled image system. 25 | class TileCoordinate 26 | { 27 | public: 28 | TileCoordinate(); 29 | 30 | // TileCoordinate(const glm::dvec2& columnRow, double zoom); 31 | 32 | /// \brief Create a tile coordinate with the given parameters. 33 | /// \param column The coordinate column. 34 | /// \param row The coordinate row. 35 | /// \param zoom The coordinate zoom level. 36 | TileCoordinate(double column, 37 | double row, 38 | double zoom); 39 | 40 | /// \brief Set the tile column. 41 | /// \param column The column value to set. 42 | void setColumn(double column); 43 | 44 | /// \returns the current column. 45 | double getColumn() const; 46 | 47 | /// \returns the normalized column at the current zoom level. 48 | double getNormalizedColumn() const; 49 | 50 | /// \returns the normalized and floored column at the current zoom level. 51 | int64_t getFlooredColumn() const; 52 | 53 | /// \brief Set the tile row. 54 | /// \param column The row value to set. 55 | void setRow(double row); 56 | 57 | /// \returns the current row. 58 | double getRow() const; 59 | 60 | /// \returns the normalized row at the current zoom level. 61 | double getNormalizedRow() const; 62 | 63 | /// \returns the normalized and floored row at the current zoom level. 64 | int64_t getFlooredRow() const; 65 | 66 | /// \brief Set the zoom level. 67 | /// \param zoom The zoom value to set. 68 | void setZoom(double zoom); 69 | 70 | /// \returns the current zoom level. 71 | double getZoom() const; 72 | 73 | /// \returns the scale for the current zoom. 74 | double scaleForZoom() const; 75 | 76 | /// \brief Get the normalized and floored zoom level. 77 | int64_t getFlooredZoom() const; 78 | 79 | /// \brief Zoom thiscoordinate to the given zoom. 80 | /// \param zoom The zoom level to zoom to. 81 | void zoomTo(double zoom); 82 | 83 | /// \brief Get this coordinate at the given zoom level. 84 | /// \param zoom The zoom level to zoom to. 85 | /// \returns a copy of this coordinate at the given zoom level. 86 | TileCoordinate getZoomedTo(double zoom) const; 87 | 88 | /// \brief Zoom this coordinate by a given zoom amount. 89 | /// \param zoom The zoom level to zoom by. 90 | void zoomBy(double zoom); 91 | 92 | /// \brief Get this coordinate zoomed by the given zoom level. 93 | /// \param zoom The zoom level to zoom by. 94 | /// \returns a copy of this coordinate zoomed by the given zoom level. 95 | TileCoordinate getZoomedBy(double zoom) const; 96 | 97 | TileCoordinate moveRightBy(double distance); 98 | TileCoordinate moveLeftBy(double distance); 99 | TileCoordinate moveUpBy(double distance); 100 | TileCoordinate moveDownBy(double distance); 101 | 102 | TileCoordinate getNeighbor(double columnDistance, double rowDistance) const; 103 | 104 | TileCoordinate getNeighborRight() const; 105 | TileCoordinate getNeighborLeft() const; 106 | TileCoordinate getNeighborUp() const; 107 | TileCoordinate getNeighborDown() const; 108 | 109 | /// \brief Get the normalized version of this tile's coordinates. 110 | /// \throws InvalidArgumentException if the zoom is < 0. 111 | /// \returns the normalized tile coordinate. 112 | TileCoordinate getNormalizedTileCoordinate() const; 113 | 114 | // TileCoordinate getFlooredRowAndColumn() const; 115 | 116 | TileCoordinate getClampedRowAndColumn() const; 117 | 118 | 119 | /// \brief Get the scaling value for the given zoom. 120 | /// 121 | /// This is equivalent to double scale = std::pow(2.0, zoom); 122 | /// 123 | /// \param zoom The integer zoom level to get the scale for. 124 | /// \returns the scale for the given zoom. 125 | static double getScaleForZoom(int zoom); 126 | 127 | /// \brief This sorts tiles by zoom and row / column and zoom. 128 | /// \param coordinate The coordinate to compare. 129 | /// \returns true if the this coordinate is less than the other. 130 | bool operator < (const TileCoordinate& coordinate) const; 131 | 132 | /// \brief This compares two tiles for equality. 133 | /// \param coordinate The coordinate to compare. 134 | /// \returns true if the this coordinate is equal to the other. 135 | bool operator == (const TileCoordinate& coordinate) const; 136 | 137 | /// \brief This sets one tile equal to another. 138 | /// \param coordinate The to set equal to. 139 | /// \returns a reference to this update coordinate. 140 | TileCoordinate& operator = (const TileCoordinate& coordinate); 141 | 142 | /// \brief Get a debug string. 143 | /// \param precision The number decimal places to use. 144 | /// \returns a debug string with the given precision. 145 | std::string toString(int precision = 8) const; 146 | 147 | /// \returns a non-cryptographic hash. 148 | std::size_t hash() const; 149 | 150 | /// \brief Stream output. 151 | /// \param os the std::ostream. 152 | /// \param coordinate The TileCoordinate to output. 153 | /// \returns the updated std::ostream reference. 154 | friend std::ostream& operator << (std::ostream& os, 155 | const TileCoordinate& coordinate); 156 | 157 | private: 158 | /// \brief The column value. 159 | /// 160 | /// This usually corresponds to the "x" position. 161 | double _column = 0.0; 162 | 163 | /// \brief The row value. 164 | /// 165 | /// This usually corresponds to the "y" position. 166 | double _row = 0.0; 167 | 168 | /// \brief The tile value. 169 | /// 170 | /// This usually corresponds to the "z" position. 171 | double _zoom = 0.0; 172 | 173 | }; 174 | 175 | 176 | inline std::ostream& operator<<(std::ostream& os, const TileCoordinate& coordinate) 177 | { 178 | os << coordinate.toString(); 179 | return os; 180 | } 181 | 182 | 183 | } } // namespace ofx::Maps 184 | 185 | 186 | namespace std { 187 | 188 | 189 | template <> struct hash 190 | { 191 | size_t operator()(const ofx::Maps::TileCoordinate& coordinate) const 192 | { 193 | return coordinate.hash(); 194 | } 195 | }; 196 | 197 | 198 | } // namespace std 199 | -------------------------------------------------------------------------------- /libs/ofxMaps/includes/ofx/Maps/TileKey.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2014 Christopher Baker 3 | // 4 | // SPDX-License-Identifier: MIT 5 | // 6 | 7 | 8 | #pragma once 9 | 10 | 11 | #include 12 | 13 | 14 | namespace ofx { 15 | namespace Maps { 16 | 17 | 18 | /// \brief A unique tile identifier. 19 | class TileKey 20 | { 21 | public: 22 | /// \brief Create an empty TileKey. 23 | TileKey(); 24 | 25 | /// \brief Create a TileKey by copying another. 26 | TileKey(const TileKey& key); 27 | 28 | /// \brief Create a TileKey. 29 | /// \param column The column to use. 30 | /// \param row The row to use. 31 | /// \param zoom The zoom level to use. 32 | /// \param providerId The provider id to use. 33 | /// \param setId The set id to use. 34 | /// \param tileId The tile id to use. 35 | TileKey(int64_t column, 36 | int64_t row, 37 | int64_t zoom, 38 | // const std::string& providerId, 39 | const std::string& setId = DEFAULT_SET_ID, 40 | const std::string& tileId = DEFAULT_TILE_ID); 41 | 42 | virtual ~TileKey(); 43 | 44 | /// \returns the column. 45 | int64_t column() const; 46 | 47 | /// \returns the row. 48 | int64_t row() const; 49 | 50 | /// \returns the zoom level. 51 | int64_t zoom() const; 52 | 53 | // /// \returns the provider id. 54 | // std::string providerId() const; 55 | 56 | /// \returns the set id. 57 | std::string setId() const; 58 | 59 | /// \returns the tile id. 60 | std::string tileId() const; 61 | 62 | /// \returns a debug string representation. 63 | std::string toString() const; 64 | 65 | /// \returns a non-cryptographic hash. 66 | std::size_t hash() const; 67 | 68 | /// \brief Stream output. 69 | /// \param os the std::ostream. 70 | /// \param coordinate The TileKey to output. 71 | /// \returns the updated std::ostream reference. 72 | friend std::ostream& operator << (std::ostream& os, 73 | const TileKey& key); 74 | 75 | 76 | /// \brief This sorts tile keys. 77 | bool operator < (const TileKey& coordinate) const; 78 | 79 | /// \brief The default set id (default is empty). 80 | static const std::string DEFAULT_SET_ID; 81 | 82 | /// \brief The default tile id (default is empty). 83 | static const std::string DEFAULT_TILE_ID; 84 | 85 | private: 86 | /// \brief The tile column. 87 | int64_t _column = 0; 88 | 89 | /// \brief The tile row. 90 | int64_t _row = 0; 91 | 92 | /// \brief The tile zoom level. 93 | int64_t _zoom = 0; 94 | 95 | // /// \brief This is the id of the provider that generated this tile coordinate. 96 | // /// 97 | // /// Default value is an empty string. 98 | // std::string _providerId; 99 | 100 | /// \brief This is the set id. 101 | /// 102 | /// Usually this is empty which indicates that it is part of a the default 103 | /// set for the given providerId. 104 | /// 105 | /// This is often set for a given set of tiled images used in street-view 106 | /// images, etc. 107 | /// 108 | /// Default value is an empty string. 109 | std::string _setId; 110 | 111 | /// \brief The unique tile id. 112 | /// 113 | /// This is usually used as the unique identifier for a given tile. For 114 | /// instance, this might be the unique ETag delivered in a cacheable web 115 | /// request. This might also be used to connect loaded pixels back to 116 | /// the raw buffered bytes. 117 | /// 118 | /// Default value is an empty string. 119 | std::string _tileId; 120 | 121 | }; 122 | 123 | 124 | inline std::ostream& operator<<(std::ostream& os, const TileKey& key) 125 | { 126 | os << key.toString(); 127 | return os; 128 | } 129 | 130 | 131 | } } // namespace ofx::Maps 132 | 133 | 134 | namespace std { 135 | 136 | 137 | template <> struct hash 138 | { 139 | size_t operator()(const ofx::Maps::TileKey& key) const 140 | { 141 | return key.hash(); 142 | } 143 | }; 144 | 145 | 146 | } // namespace std 147 | -------------------------------------------------------------------------------- /libs/ofxMaps/includes/ofx/Maps/TileTemplate.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2014 Christopher Baker 3 | // 4 | // SPDX-License-Identifier: MIT 5 | // 6 | 7 | 8 | #pragma once 9 | 10 | 11 | #include "Poco/RegularExpression.h" 12 | 13 | 14 | namespace ofx { 15 | namespace Maps { 16 | 17 | 18 | class TileTemplate 19 | { 20 | public: 21 | /// \brief A utility method for extracting parameters from a template. 22 | /// \param URITemplate The URI template to extract. 23 | static std::vector extractTemplateParameters(const std::string& URITemplate); 24 | 25 | /// \brief Converts tile coordinates into a QuadKey at a specified level of detail. 26 | /// \param tileX Tile X coordinate. 27 | /// \param tileY Tile Y coordinate. 28 | /// \param levelOfDetail Level of detail, from 1 (lowest detail) to 23 (highest detail). 29 | /// \returns a string containing the QuadKey. 30 | /// \sa https://msdn.microsoft.com/en-us/library/bb259689.aspx 31 | static std::string tileCoordinateToQuadKey(int tileX, 32 | int tileY, 33 | int levelOfDetail); 34 | 35 | /// \brief Converts a QuadKey into tile XY coordinates. 36 | /// \param quadKey QuadKey of the tile. 37 | /// \param tileX Output parameter receiving the tile X coordinate. 38 | /// \param tileY Output parameter receiving the tile Y coordinate. 39 | /// \param levelOfDetail Output parameter receiving the level of detail 40 | /// \returns true if a valid quad key was used. 41 | /// \sa https://msdn.microsoft.com/en-us/library/bb259689.aspx 42 | static bool quadKeyToTileCoordinate(const std::string& quadKey, 43 | int& tileX, 44 | int& tileY, 45 | int& levelOfDetail); 46 | 47 | /// \brief A regular expression used for extracting the template parameters. 48 | /// 49 | /// This template specifically searches for single mustache-style 50 | /// (e.g. {PARAMETER}) template parameters in the URI templates. 51 | static const Poco::RegularExpression TEMPLATE_PARAM_REGEX; 52 | 53 | static const std::string TEMPLATE_PARAM_QUADKEY; 54 | static const std::string TEMPLATE_PARAM_ZOOM; 55 | static const std::string TEMPLATE_PARAM_X; 56 | static const std::string TEMPLATE_PARAM_Y; 57 | static const std::string TEMPLATE_PARAM_TILE_ID; 58 | static const std::string TEMPLATE_PARAM_SET_ID; 59 | 60 | }; 61 | 62 | 63 | 64 | } } // namespace ofx::Maps 65 | -------------------------------------------------------------------------------- /libs/ofxMaps/src/BaseProjection.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2014 Christopher Baker 3 | // Copyright (c) -2014 Tom Carden 4 | // 5 | // SPDX-License-Identifier: MIT 6 | // 7 | 8 | 9 | #include "ofx/Maps/BaseProjection.h" 10 | #include "ofx/Maps/TileCoordinate.h" 11 | 12 | 13 | namespace ofx { 14 | namespace Maps { 15 | 16 | 17 | Transformation::Transformation() 18 | { 19 | } 20 | 21 | 22 | Transformation::Transformation(double _ax, 23 | double _bx, 24 | double _cx, 25 | double _ay, 26 | double _by, 27 | double _cy): 28 | ax(_ax), 29 | bx(_bx), 30 | cx(_cx), 31 | ay(_ay), 32 | by(_by), 33 | cy(_cy) 34 | { 35 | } 36 | 37 | 38 | Transformation::Transformation(double a1x, double a1y, 39 | double a2x, double a2y, 40 | double b1x, double b1y, 41 | double b2x, double b2y, 42 | double c1x, double c1y, 43 | double c2x, double c2y) 44 | { 45 | glm::dvec3 x = linearSolution(a1x, a1y, a2x, 46 | b1x, b1y, b2x, 47 | c1x, c1y, c2x); 48 | 49 | ax = x.x; 50 | bx = x.y; 51 | cx = x.z; 52 | 53 | glm::dvec3 y = linearSolution(a1x, a1y, a2y, 54 | b1x, b1y, b2y, 55 | c1x, c1y, c2y); 56 | 57 | ay = y.x; 58 | by = y.y; 59 | cy = y.z; 60 | } 61 | 62 | 63 | glm::dvec2 Transformation::transform(const glm::dvec2& point) const 64 | { 65 | // y = ay * point.x + by*point.y + cy); 66 | // x = ax * point.x + bx*point.y + cx 67 | 68 | 69 | double x = ay * point.x + by * point.y + cy; 70 | double y = ax * point.x + bx * point.y + cx; 71 | 72 | return glm::dvec2(x, y); 73 | } 74 | 75 | 76 | glm::dvec2 Transformation::untransform(const glm::dvec2& point) const 77 | { 78 | double x = (point.x * by - point.y * bx - cx * by + cy * bx) / (ax * by - ay * bx); 79 | double y = (point.x * ay - point.y * ax - cx * ay + cy * ax) / (bx * ay - by * ax); 80 | 81 | // x = point.x*by - point.y*bx - cx*by + cy*bx) / (ax*by - ay*bx) 82 | // y = point.x*ay - point.y*ax - cx*ay + cy*ax) / (bx*ay - by*ax) 83 | 84 | return glm::dvec2(x, y); 85 | } 86 | 87 | 88 | glm::dvec3 Transformation::linearSolution(double r1, double s1, double t1, 89 | double r2, double s2, double t2, 90 | double r3, double s3, double t3) 91 | { 92 | glm::dvec3 result; 93 | 94 | result.x = (((t2 - t3) * (s1 - s2)) - ((t1 - t2) * (s2 - s3))) 95 | / (((r2 - r3) * (s1 - s2)) - ((r1 - r2) * (s2 - s3))); 96 | 97 | result.y = (((t2 - t3) * (r1 - r2)) - ((t1 - t2) * (r2 - r3))) 98 | / (((s2 - s3) * (r1 - r2)) - ((s1 - s2) * (r2 - r3))); 99 | 100 | result.z = t1 - (r1 * result.x) - (s1 * result.y); 101 | 102 | return result; 103 | }; 104 | 105 | 106 | BaseProjection::BaseProjection(const std::string& name, 107 | double zoom, 108 | const Transformation& transformation): 109 | _name(name), 110 | _zoom(zoom), 111 | _transformation(transformation) 112 | { 113 | } 114 | 115 | 116 | BaseProjection::~BaseProjection() 117 | { 118 | } 119 | 120 | 121 | std::string BaseProjection::name() const 122 | { 123 | return _name; 124 | } 125 | 126 | 127 | double BaseProjection::zoom() const 128 | { 129 | return _zoom; 130 | } 131 | 132 | 133 | glm::dvec2 BaseProjection::project(const glm::dvec2& point) const 134 | { 135 | return _transformation.transform(rawProject(point)); 136 | } 137 | 138 | 139 | glm::dvec2 BaseProjection::unproject(const glm::dvec2& point) const 140 | { 141 | return rawUnproject(_transformation.untransform(point)); 142 | } 143 | 144 | 145 | TileCoordinate BaseProjection::geoToWorld(const Geo::Coordinate& location) const 146 | { 147 | glm::dvec2 point = project(glm::dvec2(location.getLongitudeRad(), 148 | location.getLatitudeRad())); 149 | 150 | return TileCoordinate(point.y, point.x, _zoom); 151 | } 152 | 153 | 154 | Geo::Coordinate BaseProjection::tileToGeo(const TileCoordinate& coordinate) const 155 | { 156 | auto newCoordinate = coordinate.getZoomedTo(_zoom); 157 | 158 | glm::dvec2 point = glm::degrees(unproject(glm::dvec2(newCoordinate.getColumn(), 159 | newCoordinate.getRow()))); 160 | 161 | return Geo::Coordinate(point.y, point.x); 162 | } 163 | 164 | 165 | } } // namespace ofx::Maps 166 | -------------------------------------------------------------------------------- /libs/ofxMaps/src/MBTilesCache.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2014 Christopher Baker 3 | // 4 | // SPDX-License-Identifier: MIT 5 | // 6 | 7 | 8 | #include "ofx/Maps/MBTilesCache.h" 9 | #include "Poco/SHA1Engine.h" 10 | #include "ofImage.h" 11 | #include "ofUtils.h" 12 | 13 | 14 | namespace ofx { 15 | namespace Maps { 16 | 17 | 18 | const std::string MBTilesMetadata::KEY_MIN_ZOOM = "minzoom"; 19 | const std::string MBTilesMetadata::KEY_MAX_ZOOM = "maxzoom"; 20 | const std::string MBTilesMetadata::KEY_BOUNDS = "bounds"; 21 | const std::string MBTilesMetadata::KEY_CENTER = "center"; 22 | const std::string MBTilesMetadata::KEY_NAME = "name"; 23 | const std::string MBTilesMetadata::KEY_TYPE = "type"; 24 | const std::string MBTilesMetadata::KEY_VERSION = "version"; 25 | const std::string MBTilesMetadata::KEY_DESCRIPTION = "description"; 26 | const std::string MBTilesMetadata::KEY_ATTRIBUTION = "attribution"; 27 | const std::string MBTilesMetadata::KEY_FORMAT = "format"; 28 | 29 | 30 | MBTilesMetadata::MBTilesMetadata() 31 | { 32 | } 33 | 34 | 35 | MBTilesMetadata::~MBTilesMetadata() 36 | { 37 | } 38 | 39 | 40 | int MBTilesMetadata::minZoom() const 41 | { 42 | return ofToInt(get(KEY_MIN_ZOOM, "0")); 43 | } 44 | 45 | 46 | 47 | int MBTilesMetadata::maxZoom() const 48 | { 49 | return ofToInt(get(KEY_MAX_ZOOM, "0")); 50 | } 51 | 52 | 53 | Geo::CoordinateBounds MBTilesMetadata::bounds() const 54 | { 55 | try 56 | { 57 | auto tokens = ofSplitString(get(KEY_BOUNDS), ",", true, true); 58 | return Geo::CoordinateBounds(Geo::Coordinate(ofToDouble(tokens[0]), 59 | ofToDouble(tokens[1])), 60 | Geo::Coordinate(ofToDouble(tokens[2]), 61 | ofToDouble(tokens[3]))); 62 | } 63 | catch (const Poco::NotFoundException&) 64 | { 65 | return Geo::CoordinateBounds(Geo::Coordinate(-180.0, -85), 66 | Geo::Coordinate(180, 85)); 67 | } 68 | } 69 | 70 | 71 | TileCoordinate MBTilesMetadata::center() const 72 | { 73 | try 74 | { 75 | auto tokens = ofSplitString(get(KEY_CENTER), ",", true, true); 76 | return TileCoordinate(ofToDouble(tokens[0]), 77 | ofToDouble(tokens[1]), 78 | ofToDouble(tokens[2])); 79 | } 80 | catch (const Poco::NotFoundException&) 81 | { 82 | return TileCoordinate(); 83 | } 84 | } 85 | 86 | 87 | 88 | std::string MBTilesMetadata::attribution() const 89 | { 90 | return get(KEY_ATTRIBUTION, ""); 91 | } 92 | 93 | 94 | 95 | std::string MBTilesMetadata::name() const 96 | { 97 | return get(KEY_NAME, ""); 98 | } 99 | 100 | 101 | 102 | std::string MBTilesMetadata::type() const 103 | { 104 | return get(KEY_TYPE, ""); 105 | } 106 | 107 | 108 | std::string MBTilesMetadata::version() const 109 | { 110 | return get(KEY_VERSION, ""); 111 | } 112 | 113 | 114 | std::string MBTilesMetadata::description() 115 | { 116 | return get(KEY_DESCRIPTION, ""); 117 | } 118 | 119 | 120 | 121 | std::string MBTilesMetadata::format() const 122 | { 123 | return get(KEY_FORMAT, ""); 124 | } 125 | 126 | 127 | const std::string MBTilesConnection::QUERY_SELECT_METADATA = "SELECT * FROM `metadata`"; 128 | const std::string MBTilesConnection::QUERY_INSERT_METADATA = "INSERT INTO `metadata` (`name`, `value`) VALUES (:name, :value)"; 129 | const std::string MBTilesConnection::CREATE_TABLE_METADATA = "CREATE TABLE IF NOT EXISTS metadata (name TEXT, value TEXT)"; 130 | const std::string MBTilesConnection::DROP_TABLE_METADATA = "DROP TABLE IF EXISTS metadata"; 131 | 132 | const std::string MBTilesConnection::QUERY_TILES = "SELECT tile_data FROM `tiles` WHERE zoom_level = :zoom_level AND tile_column = :tile_column AND tile_row = :tile_row"; 133 | const std::string MBTilesConnection::QUERY_TILES_WITH_SET_ID = QUERY_TILES + " AND set_id = :set_id"; 134 | 135 | const std::string MBTilesConnection::COUNT_TILES = "SELECT COUNT(tile_data) FROM `tiles` WHERE zoom_level = :zoom_level AND tile_column = :tile_column AND tile_row = :tile_row"; 136 | const std::string MBTilesConnection::COUNT_TILES_WITH_SET_ID = COUNT_TILES + " AND set_id = :set_id"; 137 | 138 | //const std::string MBTilesConnection::INSERT_IMAGE = "INSERT OR IGNORE INTO `images` (`tile_id`, `tile_data`) VALUES (:tile_id, :tile_data)"; 139 | const std::string MBTilesConnection::INSERT_IMAGE = "INSERT INTO `images` (`tile_id`, `tile_data`) VALUES (:tile_id, :tile_data)"; 140 | 141 | const std::string MBTilesConnection::COUNT_IMAGE = "SELECT COUNT(tile_id) FROM `images` WHERE tile_id = :tile_id"; 142 | 143 | const std::string MBTilesConnection::INSERT_MAP = "INSERT INTO `map` (`zoom_level`, `tile_column`, `tile_row`, `tile_id`) VALUES (:zoom_level, :tile_column, :tile_row, :tile_id)"; 144 | const std::string MBTilesConnection::INSERT_MAP_WITH_SET_ID = "INSERT INTO `map` (`zoom_level`, `tile_column`, `tile_row`, `tile_id`, `set_id`) VALUES (:zoom_level, :tile_column, :tile_row, :tile_id, :set_id)"; 145 | 146 | const std::string MBTilesConnection::COUNT_MAP = "SELECT COUNT(tile_id) FROM `map` WHERE zoom_level = :zoom_level AND tile_column = :tile_column AND tile_row = :tile_row"; 147 | const std::string MBTilesConnection::COUNT_MAP_WITH_SET_ID = COUNT_MAP + " AND set_id = :set_id"; 148 | 149 | const std::string MBTilesConnection::COUNT_ALL = "SELECT COUNT(*) FROM `tiles`"; 150 | 151 | 152 | 153 | //"-- via https://github.com/mapbox/node-mbtiles/blob/master/lib/schema.sql" 154 | // Several additions to assist with caching. 155 | // images table - added tile_expires_date and tile_cached_date 156 | // tiles - 157 | // images.tile_data AS tile_data," 158 | // images.tile_expires_date AS tile_expires_date," 159 | // images.tile_cached_date AS tile_cached_date" 160 | 161 | const std::string MBTilesConnection::MBTILES_SCHEMA = 162 | "CREATE TABLE IF NOT EXISTS map (" 163 | " zoom_level INTEGER," 164 | " tile_column INTEGER," 165 | " tile_row INTEGER," 166 | " tile_id TEXT," 167 | " set_id TEXT," 168 | " grid_id TEXT" 169 | ");" 170 | "" 171 | "CREATE TABLE IF NOT EXISTS grid_key (" 172 | " grid_id TEXT," 173 | " key_name TEXT" 174 | ");" 175 | "" 176 | "CREATE TABLE IF NOT EXISTS keymap (" 177 | " key_name TEXT," 178 | " key_json TEXT" 179 | ");" 180 | "" 181 | "CREATE TABLE IF NOT EXISTS grid_utfgrid (" 182 | " grid_id TEXT," 183 | " grid_utfgrid BLOB" 184 | ");" 185 | "" 186 | "CREATE TABLE IF NOT EXISTS images (" 187 | " tile_data blob," 188 | " tile_id text" 189 | ");" 190 | "" 191 | "CREATE TABLE IF NOT EXISTS metadata (" 192 | " name text," 193 | " value text" 194 | ");" 195 | "" 196 | "CREATE TABLE IF NOT EXISTS geocoder_data (" 197 | " type TEXT," 198 | " shard INTEGER," 199 | " data BLOB" 200 | ");" 201 | "" 202 | "CREATE UNIQUE INDEX IF NOT EXISTS map_index ON map (zoom_level, tile_column, tile_row, set_id);" 203 | "CREATE UNIQUE INDEX IF NOT EXISTS grid_key_lookup ON grid_key (grid_id, key_name);" 204 | "CREATE UNIQUE INDEX IF NOT EXISTS keymap_lookup ON keymap (key_name);" 205 | "CREATE UNIQUE INDEX IF NOT EXISTS grid_utfgrid_lookup ON grid_utfgrid (grid_id);" 206 | "CREATE UNIQUE INDEX IF NOT EXISTS images_id ON images (tile_id);" 207 | "CREATE UNIQUE INDEX IF NOT EXISTS name ON metadata (name);" 208 | "CREATE INDEX IF NOT EXISTS map_grid_id ON map (grid_id);" 209 | "CREATE INDEX IF NOT EXISTS geocoder_type_index ON geocoder_data (type);" 210 | "CREATE UNIQUE INDEX IF NOT EXISTS geocoder_shard_index ON geocoder_data (type, shard);" 211 | "" 212 | "CREATE VIEW IF NOT EXISTS tiles AS" 213 | " SELECT" 214 | " map.zoom_level AS zoom_level," 215 | " map.tile_column AS tile_column," 216 | " map.tile_row AS tile_row," 217 | " map.set_id AS set_id," 218 | " images.tile_data AS tile_data" 219 | " FROM map" 220 | " JOIN images ON images.tile_id = map.tile_id;" 221 | "" 222 | "CREATE VIEW IF NOT EXISTS grids AS" 223 | " SELECT" 224 | " map.zoom_level AS zoom_level," 225 | " map.tile_column AS tile_column," 226 | " map.tile_row AS tile_row," 227 | " map.set_id AS set_id," 228 | " grid_utfgrid.grid_utfgrid AS grid" 229 | " FROM map" 230 | " JOIN grid_utfgrid ON grid_utfgrid.grid_id = map.grid_id;" 231 | "" 232 | "CREATE VIEW IF NOT EXISTS grid_data AS" 233 | " SELECT" 234 | " map.zoom_level AS zoom_level," 235 | " map.tile_column AS tile_column," 236 | " map.tile_row AS tile_row," 237 | " map.set_id AS set_id," 238 | " keymap.key_name AS key_name," 239 | " keymap.key_json AS key_json" 240 | " FROM map" 241 | " JOIN grid_key ON map.grid_id = grid_key.grid_id" 242 | " JOIN keymap ON grid_key.key_name = keymap.key_name;" 243 | ""; 244 | 245 | //"CREATE TABLE IF NOT EXISTS map (" 246 | //" zoom_level INTEGER," 247 | //" tile_column INTEGER," 248 | //" tile_row INTEGER," 249 | //" tile_id TEXT," 250 | //" grid_id TEXT" 251 | //");" 252 | //"" 253 | //"CREATE TABLE IF NOT EXISTS grid_key (" 254 | //" grid_id TEXT," 255 | //" key_name TEXT" 256 | //");" 257 | //"" 258 | //"CREATE TABLE IF NOT EXISTS keymap (" 259 | //" key_name TEXT," 260 | //" key_json TEXT" 261 | //");" 262 | //"" 263 | //"CREATE TABLE IF NOT EXISTS grid_utfgrid (" 264 | //" grid_id TEXT," 265 | //" grid_utfgrid BLOB" 266 | //");" 267 | //"" 268 | //"CREATE TABLE IF NOT EXISTS images (" 269 | //" tile_data blob," 270 | //" tile_id text," 271 | //" tile_set_id text," 272 | //" tile_expires_date text," 273 | //" tile_cached_date text" 274 | //");" 275 | //"" 276 | //"CREATE TABLE IF NOT EXISTS metadata (" 277 | //" name text," 278 | //" value text" 279 | //");" 280 | //"" 281 | //"CREATE TABLE IF NOT EXISTS geocoder_data (" 282 | //" type TEXT," 283 | //" shard INTEGER," 284 | //" data BLOB" 285 | //");" 286 | //"" 287 | //"CREATE UNIQUE INDEX IF NOT EXISTS map_index ON map (zoom_level, tile_column, tile_row);" 288 | //"CREATE UNIQUE INDEX IF NOT EXISTS grid_key_lookup ON grid_key (grid_id, key_name);" 289 | //"CREATE UNIQUE INDEX IF NOT EXISTS keymap_lookup ON keymap (key_name);" 290 | //"CREATE UNIQUE INDEX IF NOT EXISTS grid_utfgrid_lookup ON grid_utfgrid (grid_id);" 291 | //"CREATE UNIQUE INDEX IF NOT EXISTS images_id ON images (tile_id);" 292 | //"CREATE UNIQUE INDEX IF NOT EXISTS images_set_id ON images (tile_set_id);" 293 | //"CREATE UNIQUE INDEX IF NOT EXISTS name ON metadata (name);" 294 | //"CREATE INDEX IF NOT EXISTS map_grid_id ON map (grid_id);" 295 | //"CREATE INDEX IF NOT EXISTS geocoder_type_index ON geocoder_data (type);" 296 | //"CREATE UNIQUE INDEX IF NOT EXISTS geocoder_shard_index ON geocoder_data (type, shard);" 297 | //"" 298 | //"CREATE VIEW IF NOT EXISTS tiles AS" 299 | //" SELECT" 300 | //" map.zoom_level AS zoom_level," 301 | //" map.tile_column AS tile_column," 302 | //" map.tile_row AS tile_row," 303 | //" images.tile_id AS tile_id," 304 | //" images.tile_set_id AS tile_set_id," 305 | //" images.tile_data AS tile_data," 306 | //" images.tile_expires_date AS tile_expires_date," 307 | //" images.tile_cached_date AS tile_cached_date" 308 | //" FROM map" 309 | //" JOIN images ON images.tile_id = map.tile_id;" 310 | //"" 311 | //"CREATE VIEW IF NOT EXISTS grids AS" 312 | //" SELECT" 313 | //" map.zoom_level AS zoom_level," 314 | //" map.tile_column AS tile_column," 315 | //" map.tile_row AS tile_row," 316 | //" grid_utfgrid.grid_utfgrid AS grid" 317 | //" FROM map" 318 | //" JOIN grid_utfgrid ON grid_utfgrid.grid_id = map.grid_id;" 319 | //"" 320 | //"CREATE VIEW IF NOT EXISTS grid_data AS" 321 | //" SELECT" 322 | //" map.zoom_level AS zoom_level," 323 | //" map.tile_column AS tile_column," 324 | //" map.tile_row AS tile_row," 325 | //" keymap.key_name AS key_name," 326 | //" keymap.key_json AS key_json" 327 | //" FROM map" 328 | //" JOIN grid_key ON map.grid_id = grid_key.grid_id" 329 | //" JOIN keymap ON grid_key.key_name = keymap.key_name;" 330 | //""; 331 | 332 | 333 | //MBTilesConnection::MBTilesConnection(const std::string& filename, 334 | // Mode mode, 335 | // uint64_t databaseTimeoutMilliseconds, 336 | // Poco::RWLock* mutex): 337 | // SQLite::SQLiteConnection(filename, 338 | // mode, 339 | // databaseTimeoutMilliseconds, 340 | // mutex) 341 | //{ 342 | //} 343 | 344 | 345 | MBTilesConnection::~MBTilesConnection() 346 | { 347 | } 348 | 349 | 350 | bool MBTilesConnection::setMetaData(const MBTilesMetadata& metadata) noexcept 351 | { 352 | if (_mode != Mode::READ_ONLY) 353 | { 354 | try 355 | { 356 | SQLite::Transaction transaction(_database); 357 | 358 | // Delete the old table. 359 | _database.exec(DROP_TABLE_METADATA); 360 | 361 | // Recreate it. 362 | _database.exec(CREATE_TABLE_METADATA); 363 | 364 | auto& query = getStatement(QUERY_INSERT_METADATA); 365 | 366 | for (const auto& entry: metadata) 367 | { 368 | query.bind(":name", entry.first); 369 | query.bind(":value", entry.second); 370 | query.exec(); 371 | query.reset(); 372 | } 373 | 374 | // Commit all values. 375 | transaction.commit(); 376 | 377 | return true; 378 | } 379 | catch (const std::exception& e) 380 | { 381 | ofLogError("MBTilesConnection::setMetaData()") << "SQLite exception: " << e.what(); 382 | return false; 383 | } 384 | } 385 | else 386 | { 387 | ofLogError("MBTilesConnection::setMetaData()") << "No setting data on a read-only database."; 388 | return false; 389 | } 390 | } 391 | 392 | 393 | MBTilesMetadata MBTilesConnection::getMetaData() const noexcept 394 | { 395 | MBTilesMetadata result; 396 | 397 | try 398 | { 399 | auto& query = getStatement(QUERY_SELECT_METADATA); 400 | 401 | while (query.executeStep()) 402 | { 403 | result.set(query.getColumn("name"), query.getColumn("value")); 404 | } 405 | } 406 | catch (const std::exception& e) 407 | { 408 | ofLogError("MBTilesConnection::getMetaData()") << "SQLite exception: " << e.what(); 409 | } 410 | 411 | return result; 412 | } 413 | 414 | 415 | bool MBTilesConnection::setTile(const TileKey& key, 416 | const ofBuffer& image) noexcept 417 | { 418 | if (_mode != Mode::READ_ONLY) 419 | { 420 | std::string tileId = key.tileId(); 421 | 422 | if (tileId.empty()) 423 | { 424 | Poco::SHA1Engine sha1; 425 | sha1.update(image.getData(), image.size()); 426 | const auto& digest = sha1.digest(); // obtain result 427 | tileId = Poco::DigestEngine::digestToHex(digest); 428 | } 429 | 430 | try 431 | { 432 | SQLite::Statement& countImage = getStatement(COUNT_IMAGE); 433 | countImage.bind(":tile_id", tileId); 434 | int result = countImage.executeStep(); 435 | auto column = countImage.getColumn(0); 436 | 437 | if (column.getInt64() == 0) 438 | { 439 | SQLite::Statement& insertImage = getStatement(INSERT_IMAGE); 440 | insertImage.bind(":tile_id", tileId); 441 | insertImage.bind(":tile_data", image.getData(), image.size()); 442 | 443 | if (insertImage.exec() != 1) 444 | { 445 | ofLogError("MBTilesConnection::setTile()") << "Unable to insert image."; 446 | return false; 447 | } 448 | } 449 | } 450 | catch (const std::exception& e) 451 | { 452 | ofLogError("MBTilesConnection::setTile()") << "INSERTING TILE SQLite exception: " << e.what() << " " << index() << " " << useCount(); 453 | return false; 454 | } 455 | 456 | try 457 | { 458 | SQLite::Statement& selectMap = getStatement(key.setId().empty() ? COUNT_MAP : COUNT_MAP_WITH_SET_ID); 459 | selectMap.bind(":tile_column", key.column()); 460 | selectMap.bind(":tile_row", key.row()); 461 | selectMap.bind(":zoom_level", key.zoom()); 462 | 463 | if (!key.setId().empty()) 464 | { 465 | selectMap.bind(":set_id", key.setId()); 466 | } 467 | 468 | int result = selectMap.executeStep(); 469 | auto column = selectMap.getColumn(0); 470 | 471 | if (column.getInt64() == 0) 472 | { 473 | SQLite::Statement& insertMap = getStatement(key.setId().empty() ? INSERT_MAP : INSERT_MAP_WITH_SET_ID); 474 | insertMap.bind(":tile_column", key.column()); 475 | insertMap.bind(":tile_row", key.row()); 476 | insertMap.bind(":zoom_level", key.zoom()); 477 | insertMap.bind(":tile_id", tileId); 478 | 479 | if (!key.setId().empty()) 480 | { 481 | insertMap.bind(":set_id", key.setId()); 482 | } 483 | 484 | if (insertMap.exec() == 1) 485 | { 486 | return true; 487 | } 488 | else 489 | { 490 | ofLogError("MBTilesConnection::setTile()") << "Unable to insert map."; 491 | return false; 492 | } 493 | } 494 | else 495 | { 496 | return true; 497 | } 498 | 499 | } 500 | catch (const std::exception& e) 501 | { 502 | ofLogError("MBTilesConnection::setTile()") << "INSERTING MAP SQLite exception: " << e.what() << " " << index() << " " << useCount(); 503 | return false; 504 | } 505 | 506 | } 507 | else 508 | { 509 | ofLogError("MBTilesConnection::setTile()") << "No setting data on a read-only database."; 510 | return false; 511 | } 512 | } 513 | 514 | 515 | bool MBTilesConnection::has(const TileKey& key) const noexcept 516 | { 517 | try 518 | { 519 | SQLite::Statement& query = getStatement(key.setId().empty() ? COUNT_TILES : COUNT_TILES_WITH_SET_ID); 520 | query.bind(":tile_row", key.row()); 521 | query.bind(":zoom_level", key.zoom()); 522 | query.bind(":tile_column", key.column()); 523 | 524 | if (!key.setId().empty()) 525 | { 526 | query.bind(":set_id", key.setId()); 527 | } 528 | 529 | int result = query.executeStep(); 530 | auto column = query.getColumn(0); 531 | return column.getInt64() > 0; 532 | } 533 | catch (const std::exception& e) 534 | { 535 | ofLogError("MBTilesConnection::has()") << "SQLite exception: " << e.what(); 536 | return false; 537 | } 538 | } 539 | 540 | 541 | std::shared_ptr MBTilesConnection::getBuffer(const TileKey& key) const noexcept 542 | { 543 | try 544 | { 545 | SQLite::Statement& query = getStatement(key.setId().empty() ? QUERY_TILES : QUERY_TILES_WITH_SET_ID); 546 | 547 | query.bind(":tile_row", key.row()); 548 | query.bind(":zoom_level", key.zoom()); 549 | query.bind(":tile_column", key.column()); 550 | 551 | if (!key.setId().empty()) 552 | { 553 | query.bind(":set_id", key.setId()); 554 | } 555 | 556 | int result = query.executeStep(); 557 | 558 | if (result) 559 | { 560 | auto column = query.getColumn("tile_data"); 561 | 562 | if (column.isBlob()) 563 | { 564 | return std::make_shared(reinterpret_cast(column.getBlob()), column.getBytes()); 565 | } 566 | else 567 | { 568 | ofLogError("MBTilesConnection::getBuffer") << "Tile data existed, but wasn't a blob: " << this->database().getFilename() << " ";// << key.coordinate().toString(); 569 | return nullptr; 570 | } 571 | } 572 | else 573 | { 574 | // We simply don't have it. 575 | return nullptr; 576 | } 577 | } 578 | catch (const std::exception& e) 579 | { 580 | ofLogError("MBTilesConnection::getBuffer") << "SQLite exception: " << e.what(); 581 | return nullptr; 582 | } 583 | } 584 | 585 | 586 | size_t MBTilesConnection::size() const noexcept 587 | { 588 | try 589 | { 590 | SQLite::Statement& query = getStatement(COUNT_ALL); 591 | int result = query.executeStep(); 592 | auto column = query.getColumn(0); 593 | return static_cast(column.getInt64()); 594 | } 595 | catch (const std::exception& e) 596 | { 597 | ofLogError("MBTilesConnection::size") << "SQLite exception: " << e.what(); 598 | return 0; 599 | } 600 | } 601 | 602 | 603 | 604 | std::shared_ptr MBTilesConnection::getTile(const TileKey& key) const noexcept 605 | { 606 | auto buffer = getBuffer(key); 607 | 608 | if (buffer != nullptr) 609 | { 610 | ofPixels pixels; 611 | 612 | if (ofLoadImage(pixels, *buffer)) 613 | { 614 | return std::make_shared(pixels); 615 | } 616 | else 617 | { 618 | ofLogError("MBTilesConnection::getTile") << "Error loading pixels."; 619 | return nullptr; 620 | } 621 | } 622 | else 623 | { 624 | return nullptr; 625 | } 626 | } 627 | 628 | 629 | MBTilesCache::MBTilesCache(const MapTileProvider& tileProvider, 630 | const std::string& cachePath, 631 | uint64_t databaseTimeoutMilliseconds, 632 | std::size_t capacity, 633 | std::size_t peakCapacity) 634 | { 635 | std::filesystem::create_directories(ofToDataPath(cachePath, true)); 636 | std::filesystem::path tilePath = cachePath; 637 | tilePath /= (tileProvider.id() + ".mbtiles"); 638 | 639 | try 640 | { 641 | _writeConnection = std::make_unique(tilePath.string(), 642 | SQLite::SQLiteConnection::Mode::READ_WRITE_CREATE, 643 | databaseTimeoutMilliseconds), 644 | 645 | _readConnectionPool = std::make_unique(tilePath.string(), 646 | SQLite::SQLiteConnection::Mode::READ_ONLY, 647 | databaseTimeoutMilliseconds, 648 | capacity, 649 | peakCapacity); 650 | 651 | SQLite::Transaction transaction(_writeConnection->database()); 652 | _writeConnection->database().exec(MBTilesConnection::MBTILES_SCHEMA); 653 | transaction.commit(); 654 | 655 | _writeConnection->database().exec("PRAGMA journal_mode=WAL"); 656 | 657 | } 658 | catch (const std::exception& e) 659 | { 660 | ofLogError("MBTilesConnection::MBTilesConnection()") << "Error initializing database - SQLite exception: " << e.what(); 661 | } 662 | 663 | try 664 | { 665 | MBTilesMetadata metadata; 666 | metadata.set(MBTilesMetadata::KEY_MIN_ZOOM, std::to_string(tileProvider.minZoom())); 667 | metadata.set(MBTilesMetadata::KEY_MAX_ZOOM, std::to_string(tileProvider.maxZoom())); 668 | metadata.set(MBTilesMetadata::KEY_BOUNDS, tileProvider.bounds().toString()); 669 | metadata.set(MBTilesMetadata::KEY_CENTER, tileProvider.center().toString()); 670 | metadata.set(MBTilesMetadata::KEY_NAME, tileProvider.name()); 671 | metadata.set(MBTilesMetadata::KEY_ATTRIBUTION, tileProvider.attribution()); 672 | 673 | auto dictionary = tileProvider.dictionary(); 674 | 675 | auto dictAdd = [&](const std::string& key) 676 | { 677 | if (dictionary.find(key) != dictionary.end()) 678 | metadata.set(key, dictionary.find(key)->second); 679 | }; 680 | 681 | dictAdd(MBTilesMetadata::KEY_TYPE); 682 | dictAdd(MBTilesMetadata::KEY_VERSION); 683 | dictAdd(MBTilesMetadata::KEY_FORMAT); 684 | 685 | _writeConnection->setMetaData(metadata); 686 | } 687 | catch (const std::exception& e) 688 | { 689 | ofLogError("MBTilesConnection::MBTilesConnection()") << "Error setting metadata - SQLite exception: " << e.what(); 690 | } 691 | 692 | _writeThread = std::thread([&]() { 693 | 694 | std::pair> value; 695 | while (_writeChannel.receive(value)) 696 | { 697 | try 698 | { 699 | if (!_writeConnection->has(value.first)) 700 | { 701 | _writeConnection->setTile(value.first, *value.second); 702 | } 703 | } 704 | catch (const std::exception& e) 705 | { 706 | ofLogError("MBTilesConnection::MBTilesConnection()") << "Error setting tile - SQLite exception: " << e.what(); 707 | } 708 | } 709 | }); 710 | } 711 | 712 | 713 | MBTilesCache::~MBTilesCache() 714 | { 715 | _writeChannel.close(); 716 | _writeThread.join(); 717 | } 718 | 719 | 720 | const MBTilesCache::MBTilesConnectionPool& MBTilesCache::readConnectionPool() const 721 | { 722 | return *_readConnectionPool; 723 | } 724 | 725 | 726 | bool MBTilesCache::doHas(const TileKey& key) const 727 | { 728 | auto connection = _readConnectionPool->borrowObject(); 729 | auto result = connection->has(key); 730 | _readConnectionPool->returnObject(connection); 731 | return result; 732 | } 733 | 734 | 735 | std::shared_ptr MBTilesCache::doGet(const TileKey& key) 736 | { 737 | auto connection = _readConnectionPool->borrowObject(); 738 | auto result = connection->getBuffer(key); 739 | _readConnectionPool->returnObject(connection); 740 | return result; 741 | } 742 | 743 | 744 | void MBTilesCache::doAdd(const TileKey& key, std::shared_ptr entry) 745 | { 746 | _writeChannel.send(std::make_pair(key, entry)); 747 | } 748 | 749 | 750 | void MBTilesCache::doRemove(const TileKey& key) 751 | { 752 | ofLogError("MBTilesCache::doRemove") << "Not yet implemented."; 753 | } 754 | 755 | 756 | std::size_t MBTilesCache::doSize() 757 | { 758 | auto connection = _readConnectionPool->borrowObject(); 759 | auto result = connection->size(); 760 | _readConnectionPool->returnObject(connection); 761 | return result; 762 | } 763 | 764 | 765 | void MBTilesCache::doClear() 766 | { 767 | ofLogError("MBTilesCache::doClear") << "Not yet implemented."; 768 | } 769 | 770 | 771 | } } // namespace ofx::Maps 772 | -------------------------------------------------------------------------------- /libs/ofxMaps/src/MapNavigator.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2014 Christopher Baker 3 | // Copyright (c) -2014 Tom Carden 4 | // 5 | // SPDX-License-Identifier: MIT 6 | // 7 | 8 | 9 | // 10 | //#include "ofx/Maps/MapNavigator.h" 11 | // 12 | // 13 | //namespace ofx { 14 | //namespace Maps { 15 | // 16 | // 17 | // 18 | //void MapNavigator::keyPressed(ofKeyEventArgs& evt) 19 | //{ 20 | // int key = evt.key; 21 | // 22 | // if (key == '+' || key == '=') 23 | // { 24 | // if (getZoom() < 19) 25 | // { 26 | // zoomIn(); 27 | // } 28 | // } 29 | // else if (key == '-' || key == '_') 30 | // { 31 | // if (getZoom() > 1) 32 | // { 33 | // zoomOut(); 34 | // } 35 | // } 36 | // // TODO: keyboard movement 37 | //} 38 | // 39 | //void MapNavigator::keyReleased(ofKeyEventArgs& evt) 40 | //{ 41 | //} 42 | // 43 | // 44 | //void MapNavigator::mouseDragged(ofMouseEventArgs& evt) 45 | //{ 46 | // // ofVec2d mouse(evt.x, evt.y); 47 | // // 48 | // // int button = evt.button; 49 | // // 50 | // // ofVec2d dMouse = (mouse - _prevMouse) / _position.z; 51 | // // 52 | // // if (button == 0) 53 | // // { 54 | // // _position += dMouse; 55 | // // } 56 | // // 57 | // // _prevMouse = mouse; 58 | //} 59 | // 60 | // 61 | //void MapNavigator::mouseMoved(ofMouseEventArgs& evt) 62 | //{ 63 | //} 64 | // 65 | // 66 | //void MapNavigator::mousePressed(ofMouseEventArgs& evt) 67 | //{ 68 | // ofVec2d mouse(evt.x, evt.y); 69 | // 70 | // int button = evt.button; 71 | // 72 | // unsigned long long now = ofGetElapsedTimeMillis(); 73 | // unsigned long long dt = now - _lastClickTime; 74 | // 75 | // if (dt < DEFAULT_DOUBLE_CLICK_TIME) 76 | // { 77 | // // Should center. 78 | // setPointCenter(mouse); 79 | // zoomIn(); 80 | // } 81 | // 82 | // _lastClickTime = now; 83 | // _prevMouse = mouse; 84 | // 85 | //} 86 | //void MapNavigator::mouseReleased(ofMouseEventArgs& evt) 87 | //{ 88 | //} 89 | // 90 | // 91 | //double MapNavigator::getZoom() const 92 | //{ 93 | // return _centerTileCoordinate.getZoom(); 94 | //} 95 | // 96 | // 97 | //// sets scale according to given zoom level, should leave you with pixel perfect tiles 98 | //void MapNavigator::setZoom(double zoom) 99 | //{ 100 | // _centerTileCoordinate = _centerTileCoordinate.zoomTo(zoom); 101 | //} 102 | // 103 | // 104 | //void MapNavigator::zoom(double zoomStep) 105 | //{ 106 | // setZoom(getZoom() + zoomStep); 107 | //} 108 | // 109 | // 110 | //void MapNavigator::zoomIn() 111 | //{ 112 | // setZoom(getZoom() + 1); 113 | //} 114 | // 115 | //void MapNavigator::zoomOut() 116 | //{ 117 | // setZoom(getZoom() - 1); 118 | //} 119 | // 120 | // 121 | //void MapNavigator::panBy(const ofVec2d& delta) 122 | //{ 123 | // panBy(delta.x, delta.y); 124 | //} 125 | // 126 | // 127 | //void MapNavigator::panBy(double dx, double dy) 128 | //{ 129 | // double sinr = sin(_rotation); 130 | // double cosr = cos(_rotation); 131 | // 132 | // double dxr = dx * cosr + dy * sinr; 133 | // double dyr = dy * cosr - dx * sinr; 134 | // 135 | // ofVec2d tileSize(_provider->getTileWidth(), _provider->getTileHeight()); 136 | // 137 | // _centerTileCoordinate.setColumn(_centerTileCoordinate.getColumn() - dxr / tileSize.x); 138 | // _centerTileCoordinate.setRow(_centerTileCoordinate.getRow() - dyr / tileSize.y); 139 | //} 140 | // 141 | // 142 | //void MapNavigator::scaleBy(double s) 143 | //{ 144 | // scaleBy(s, _size * 0.5); 145 | //} 146 | // 147 | // 148 | //void MapNavigator::scaleBy(double s, const ofVec2d& c) 149 | //{ 150 | // scaleBy(s, c.x, c.y); 151 | //} 152 | // 153 | // 154 | //void MapNavigator::scaleBy(double s, double cx, double cy) 155 | //{ 156 | // const double prevRotation = _rotation; 157 | // rotateBy(-prevRotation, cx, cy); 158 | // ofVec2d center = _size * 0.5; 159 | // panBy(-cx + center.x, -cy + center.y); 160 | // _centerTileCoordinate = _centerTileCoordinate.zoomBy(log2(s)); 161 | // panBy(cx - center.x, cy - center.y); 162 | // rotateBy(prevRotation, cx, cy); 163 | //} 164 | // 165 | // 166 | //void MapNavigator::rotateBy(double r, double cx, double cy) 167 | //{ 168 | // panBy(-cx, -cy); 169 | // _rotation += r; 170 | // panBy(cx, cy); 171 | //} 172 | // 173 | // 174 | // Geo::Coordinate MapNavigator::getGeoLocationCenter() const 175 | // { 176 | // return _provider->tileCoordinateToGeoCoordinate(_centerTileCoordinate); 177 | // } 178 | // 179 | // 180 | // TileCoordinate MapNavigator::getTileCoordinateCenter() const 181 | // { 182 | // return _centerTileCoordinate; 183 | // } 184 | // 185 | // 186 | // void MapNavigator::setPointCenter(const ofVec2d& center) 187 | // { 188 | // setTileCoordinateCenter(pointToTileCoordinate(center)); 189 | // } 190 | // 191 | // 192 | // void MapNavigator::setGeoLocationCenter(const Geo::Coordinate& location) 193 | // { 194 | // setTileCoordinateCenter(_provider->geoCoordinateToTileCoordinate(location).zoomTo(getZoom())); 195 | // } 196 | // 197 | // 198 | // void MapNavigator::setTileCoordinateCenter(const TileCoordinate& center) 199 | // { 200 | // _centerTileCoordinate = center; 201 | // } 202 | // 203 | // 204 | // TileCoordinate MapNavigator::pointToTileCoordinate(const ofVec2d& point) const 205 | // { 206 | // /* Return a coordinate on the map image for a given x, y point. */ 207 | // // new point coordinate reflecting distance from map center, in tile widths 208 | // ofVec2d rotated(point); 209 | // 210 | // const ofVec2d tileSize(_provider->getTileWidth(), _provider->getTileHeight()); 211 | // 212 | // rotated.rotate(-_rotation * RAD_TO_DEG); 213 | // 214 | // TileCoordinate coord(_centerTileCoordinate); 215 | // 216 | // coord += (rotated - (_size / 2.0)) / tileSize; 217 | // 218 | // return coord; 219 | // } 220 | // 221 | // 222 | // ofVec2d MapNavigator::tileCoordinateToPoint(const TileCoordinate& target) const 223 | // { 224 | // TileCoordinate coord = target; 225 | // 226 | // if(coord.getZoom() != _centerTileCoordinate.getZoom()) 227 | // { 228 | // coord = coord.zoomTo(_centerTileCoordinate.getZoom()); 229 | // } 230 | // 231 | // // distance from the center of the map 232 | // const ofVec2d tileSize(_provider->getTileWidth(), _provider->getTileHeight()); 233 | // 234 | // ofVec2d point = _size * 0.5; 235 | // 236 | // point.x += tileSize.x * (coord.getColumn() - _centerTileCoordinate.getColumn()); 237 | // point.y += tileSize.y * (coord.getRow() - _centerTileCoordinate.getRow()); 238 | // 239 | // ofVec2d rotated(point); 240 | // 241 | // rotated.rotate(_rotation * RAD_TO_DEG); 242 | // 243 | // return rotated; 244 | // } 245 | // 246 | // 247 | // ofVec2d MapNavigator::geoLocationToPoint(const Geo::Coordinate& location) const 248 | // { 249 | // return tileCoordinateToPoint(_provider->geoCoordinateToTileCoordinate(location)); 250 | // } 251 | // 252 | // 253 | // Geo::Coordinate MapNavigator::pointToGeolocation(const ofVec2d& point) const 254 | // { 255 | // return _provider->tileCoordinateToGeoCoordinate(pointToTileCoordinate(point)); 256 | // } 257 | // 258 | // 259 | //} } // namespace ofx::Maps 260 | -------------------------------------------------------------------------------- /libs/ofxMaps/src/MapTileLayer.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2014 Christopher Baker 3 | // 4 | // SPDX-License-Identifier: MIT 5 | // 6 | 7 | 8 | #include "ofx/Maps/MapTileLayer.h" 9 | #include "ofGraphics.h" 10 | 11 | 12 | namespace ofx { 13 | namespace Maps { 14 | 15 | 16 | MapTileLayer::MapTileLayer(std::shared_ptr tiles, float width, float height): 17 | _tiles(tiles), 18 | _size(width, height), 19 | _coordsDirty(true), 20 | _center(0.5, 0.5, 0), 21 | _padding(0, 0), 22 | _fboNeedsResize(true), 23 | _onTileCachedListener(_tiles->onAdd.newListener(this, &MapTileLayer::onTileCached)), 24 | // _onTileUncachedListener(_tiles->onRemoved.newListener(this, &MapTileLayer::onTileUncached)), 25 | _onTileRequestCancelledListener(_tiles->onRequestCancelled.newListener(this, &MapTileLayer::onTileRequestCancelled)), 26 | _onTileRequestFailedListener(_tiles->onRequestFailed.newListener(this, &MapTileLayer::onTileRequestFailed)) 27 | { 28 | 29 | 30 | 31 | // double z = log2(std::min(_width, _height) / 32 | // std::min(_provider->tileWidth(), 33 | // _provider->tileHeight())); 34 | 35 | // _center = TileCoordinateUtils::zoomTo(_center, z); 36 | 37 | } 38 | 39 | 40 | MapTileLayer::~MapTileLayer() 41 | { 42 | try 43 | { 44 | cancelQueuedRequests(); 45 | } 46 | catch (const Poco::Exception& exc) 47 | { 48 | ofLogError("MapTileLayer::~MapTileLayer") << ">> " << exc.displayText(); 49 | } 50 | } 51 | 52 | 53 | void MapTileLayer::update() 54 | { 55 | if (_fboNeedsResize) 56 | { 57 | _fbo.allocate(_size.x, _size.y); 58 | _fboNeedsResize = false; 59 | } 60 | 61 | if (_coordsDirty) 62 | { 63 | _visisbleCoords = calculateVisibleCoordinates(); 64 | _coordsDirty = false; 65 | } 66 | } 67 | 68 | 69 | void MapTileLayer::draw(float x, float y) const 70 | { 71 | // _fbo.begin(); 72 | 73 | // ofClear(0, 0, 0); 74 | 75 | std::set::const_reverse_iterator iter = _visisbleCoords.rbegin(); 76 | 77 | while (iter != _visisbleCoords.rend()) 78 | { 79 | const TileCoordinate& coord = *iter; 80 | 81 | auto tile = getTile(coord); 82 | 83 | glm::vec2 position = tileToPixels(coord); 84 | auto dZoom = _center.getZoom() - coord.getZoom(); 85 | double scale = dZoom < 1 ? 1 : std::pow(2.0, dZoom); 86 | glm::dvec2 tileSize = _tiles->provider()->tileSize() * scale; 87 | ofPushMatrix(); 88 | ofTranslate(position.x, position.y); 89 | 90 | if (tile) 91 | { 92 | // ofScale(1, -1, 0); 93 | tile->draw(0, 0, tileSize.x, tileSize.y); 94 | 95 | // ofPushStyle(); 96 | // ofNoFill(); 97 | // ofSetColor(255, 0, 0, 127); 98 | // ofDrawRectangle(position.x, position.y, tileSize.x, tileSize.y); 99 | // ofPopStyle(); 100 | 101 | //ofDrawBitmapStringHighlight(coord.toString(0), position.x + 20, position.y + 20); 102 | } 103 | else 104 | { 105 | ofPushStyle(); 106 | ofNoFill(); 107 | ofSetColor(255, 127); 108 | ofDrawRectangle(0, 0, tileSize.x, tileSize.y); 109 | ofDrawLine(0, 0, tileSize.x, tileSize.y); 110 | ofDrawLine(tileSize.x, 0, 0, tileSize.y); 111 | ofPopStyle(); 112 | } 113 | ofPopMatrix(); 114 | ++iter; 115 | } 116 | 117 | // if (center.) 118 | 119 | // _fbo.end(); 120 | // 121 | // _fbo.draw(x, y); 122 | 123 | 124 | 125 | ofSetColor(255); 126 | ofNoFill(); 127 | ofDrawRectangle(0, 0, _size.x, _size.y); 128 | } 129 | 130 | 131 | void MapTileLayer::draw(float x, float y, float w, float h) const 132 | { 133 | ofPushMatrix(); 134 | ofScale(_size.x / w, _size.y / h); 135 | draw(x, y); 136 | ofPopMatrix(); 137 | } 138 | 139 | 140 | glm::vec2 MapTileLayer::getSize() const 141 | { 142 | return _size; 143 | } 144 | 145 | 146 | void MapTileLayer::setSize(const glm::vec2& size) 147 | { 148 | setWidth(size.x); 149 | setHeight(size.y); 150 | } 151 | 152 | 153 | float MapTileLayer::getWidth() const 154 | { 155 | return _size.x; 156 | } 157 | 158 | 159 | void MapTileLayer::setWidth(float width) 160 | { 161 | _size.x = width; 162 | _coordsDirty = true; 163 | _fboNeedsResize = true; 164 | } 165 | 166 | 167 | float MapTileLayer::getHeight() const 168 | { 169 | return _size.y; 170 | } 171 | 172 | 173 | void MapTileLayer::setHeight(float height) 174 | { 175 | _size.y = height; 176 | _coordsDirty = true; 177 | _fboNeedsResize = true; 178 | } 179 | 180 | 181 | const TileCoordinate& MapTileLayer::getCenter() const 182 | { 183 | return _center; 184 | } 185 | 186 | 187 | void MapTileLayer::setCenter(const TileCoordinate& center) 188 | { 189 | _center = center; 190 | _coordsDirty = true; 191 | } 192 | 193 | 194 | void MapTileLayer::setCenter(const Geo::Coordinate& center, double zoom) 195 | { 196 | setCenter(_tiles->provider()->geoToWorld(center).getZoomedTo(zoom)); 197 | } 198 | 199 | 200 | std::set MapTileLayer::calculateVisibleCoordinates() const 201 | { 202 | // Round the current zoom in case we are in between levels. 203 | int baseZoom = glm::clamp(static_cast(std::round(_center.getZoom())), 204 | _tiles->provider()->minZoom(), 205 | _tiles->provider()->maxZoom()); 206 | 207 | // Get the layers points of the current screen. 208 | glm::dvec2 topLeftPoint(0, 0); 209 | glm::dvec2 topRightPoint(_size.x, 0); 210 | glm::dvec2 bottomLeftPoint(0, _size.y); 211 | glm::dvec2 bottomRightPoint = _size; 212 | 213 | // Translate the layer points to tile coordinates, then zoom them to baseZoom. 214 | auto topLeft = pixelsToTile(topLeftPoint).getZoomedTo(baseZoom); 215 | auto topRight = pixelsToTile(topRightPoint).getZoomedTo(baseZoom); 216 | auto bottomLeft = pixelsToTile(bottomLeftPoint).getZoomedTo(baseZoom); 217 | auto bottomRight = pixelsToTile(bottomRightPoint).getZoomedTo(baseZoom); 218 | 219 | int minCol = std::floor(std::min(std::min(topLeft.getColumn(), 220 | topRight.getColumn()), 221 | std::min(bottomLeft.getColumn(), 222 | bottomRight.getColumn()))); 223 | 224 | int maxCol = std::ceil(std::max(std::max(topLeft.getColumn(), 225 | topRight.getColumn()), 226 | std::max(bottomLeft.getColumn(), 227 | bottomRight.getColumn()))); 228 | 229 | int minRow = std::floor(std::min(std::min(topLeft.getRow(), 230 | topRight.getRow()), 231 | std::min(bottomLeft.getRow(), 232 | bottomRight.getRow()))); 233 | 234 | int maxRow = std::ceil(std::max(std::max(topLeft.getRow(), 235 | topRight.getRow()), 236 | std::max(bottomLeft.getRow(), 237 | bottomRight.getRow()))); 238 | 239 | minCol -= _padding.x; 240 | minRow -= _padding.y; 241 | maxCol += _padding.x; 242 | maxRow += _padding.y; 243 | 244 | int gridSize = TileCoordinate::getScaleForZoom(baseZoom); 245 | 246 | minCol = glm::clamp(minCol, 0, gridSize); 247 | maxCol = glm::clamp(maxCol, 0, gridSize); 248 | minRow = glm::clamp(minRow, 0, gridSize); 249 | maxRow = glm::clamp(maxRow, 0, gridSize); 250 | 251 | std::set coordinatesToDraw; 252 | std::set requestedCoordinates; 253 | 254 | // Collect visible tile coordinates. 255 | 256 | for (int col = minCol; col < maxCol; ++col) 257 | { 258 | for (int row = minRow; row < maxRow; ++row) 259 | { 260 | TileCoordinate coord(col, row, baseZoom); 261 | 262 | // Do we have this tile? 263 | if (!hasTile(coord)) 264 | { 265 | requestedCoordinates.insert(coord); 266 | 267 | // // Since we don't have this coordinate, let's try to find an 268 | // // existing coordinate at a different scale that we can draw in 269 | // // its place. 270 | // 271 | // // First we look backwards from the current zoom level. 272 | // 273 | // bool foundParent = false; 274 | // 275 | // for (int i = coord.getZoom(); i >= _tiles->provider()->minZoom(); --i) 276 | // { 277 | // auto parentCoord = coord.getZoomedTo(i); 278 | // 279 | // parentCoord = TileCoordinate(parentCoord.getFlooredColumn(), 280 | // parentCoord.getFlooredRow(), 281 | // parentCoord.getZoom()); 282 | // 283 | // parentCoord = parentCoord.getClampedRowAndColumn(); 284 | // 285 | // if (!hasTile(coord)) 286 | // { 287 | // // We don't have the parent coordinate, so let's request 288 | // // it. 289 | // requestedCoordinates.insert(parentCoord); 290 | // } 291 | // else 292 | // { 293 | // // If we do have the parent coordinate, add it to the 294 | // // drawing queue. 295 | // coordinatesToDraw.insert(parentCoord); 296 | // foundParent = true; 297 | // break; 298 | // } 299 | // } 300 | 301 | 302 | // if (!foundParent) 303 | // { 304 | // TileCoordinate zoomed = coord.getZoomedBy(1); 305 | // std::vector kids; 306 | // kids.push_back(zoomed); 307 | // kids.push_back(zoomed.getCoordinateRight()); 308 | // kids.push_back(zoomed.getCoordinateDown()); 309 | // kids.push_back(zoomed.getCoordinateRight().getCoordinateDown()); 310 | // for (int i = 0; i < kids.size(); ++i) 311 | // { 312 | // coordinatesToDraw.insert(kids[i]); 313 | // } 314 | // } 315 | } 316 | else 317 | { 318 | // We have it so, add it to the drawing queue. 319 | coordinatesToDraw.insert(coord); 320 | } 321 | } 322 | } 323 | 324 | requestTiles(requestedCoordinates); 325 | 326 | return coordinatesToDraw; 327 | } 328 | 329 | 330 | TileKey MapTileLayer::keyForCoordinate(const TileCoordinate& coordinate) const 331 | { 332 | return TileKey(coordinate.getFlooredColumn(), 333 | coordinate.getFlooredRow(), 334 | coordinate.getFlooredZoom(), 335 | _setId); 336 | } 337 | 338 | 339 | bool MapTileLayer::hasTile(const TileCoordinate& coordinate) const 340 | { 341 | return _tiles->has(keyForCoordinate(coordinate)); 342 | } 343 | 344 | 345 | std::shared_ptr MapTileLayer::getTile(const TileCoordinate& coordinate) const 346 | { 347 | return _tiles->get(keyForCoordinate(coordinate)); 348 | } 349 | 350 | 351 | void MapTileLayer::cancelQueuedRequests() const 352 | { 353 | for (const auto& requestId: _outstandingRequests) 354 | { 355 | try 356 | { 357 | _tiles->cancelQueuedRequest(requestId); 358 | } 359 | catch (const std::exception& exc) 360 | { 361 | //std::cout << "<<>>" << exc.what() << std::endl; 362 | } 363 | } 364 | } 365 | 366 | 367 | void MapTileLayer::requestTiles(const std::set& coordinates) const 368 | { 369 | //cancelQueuedRequests(); 370 | 371 | std::vector coordinatesToRequest(coordinates.begin(), 372 | coordinates.end()); 373 | 374 | QueueSorter sorter(_center); 375 | 376 | std::sort(coordinatesToRequest.begin(), 377 | coordinatesToRequest.end(), 378 | sorter); 379 | 380 | for (const auto& coordinate: coordinatesToRequest) 381 | { 382 | try 383 | { 384 | auto key = keyForCoordinate(coordinate); 385 | _tiles->request(key); 386 | _outstandingRequests.insert(key); 387 | } 388 | catch (const Poco::ExistsException& exc) 389 | { 390 | //std::cout << exc.displayText() << endl; 391 | } 392 | } 393 | } 394 | 395 | 396 | void MapTileLayer::onTileCached(const std::pair>& args) 397 | { 398 | // std::cout << "tile cached!" << std::endl; 399 | _coordsDirty = true; 400 | } 401 | 402 | 403 | void MapTileLayer::onTileUncached(const TileKey& args) 404 | { 405 | // std::cout << "tile uncached!" << std::endl; 406 | // _coordsDirty = true; 407 | } 408 | 409 | 410 | void MapTileLayer::onTileRequestCancelled(const TileKey& key) 411 | { 412 | _outstandingRequests.erase(key); 413 | } 414 | 415 | 416 | void MapTileLayer::onTileRequestFailed(const Cache::RequestFailedArgs& args) 417 | { 418 | ofLogError("MapTileLayer::onTileRequestFailed") << "Failed to load " << args.key().toString() << ": " << args.error(); 419 | _outstandingRequests.erase(args.key()); 420 | } 421 | 422 | 423 | void MapTileLayer::setSetId(const std::string& setId) 424 | { 425 | _setId = setId; 426 | _coordsDirty = true; 427 | } 428 | 429 | 430 | std::string MapTileLayer::getSetId() const 431 | { 432 | return _setId; 433 | } 434 | 435 | 436 | TileCoordinate MapTileLayer::pixelsToTile(const glm::vec2& pixelCoordinate) const 437 | { 438 | glm::dvec2 factor = (pixelCoordinate - (_size * 0.5)) / _tiles->provider()->tileSize(); 439 | 440 | return _center.getNeighbor(factor.x, factor.y); 441 | 442 | // return TileCoordinate(_center.getColumn() + factor.x, 443 | // _center.getRow() + factor.y, 444 | // _center.getZoom()); 445 | } 446 | 447 | 448 | glm::vec2 MapTileLayer::tileToPixels(const TileCoordinate& tileCoordinate) const 449 | { 450 | glm::dvec2 pixelCenter = _size * 0.5; 451 | 452 | double scale = std::pow(2.0, _center.getZoom() - tileCoordinate.getZoom()); 453 | 454 | glm::dvec2 scaledTileSize = _tiles->provider()->tileSize() * scale; 455 | 456 | TileCoordinate zoomedCenterCoordinate = _center.getZoomedTo(tileCoordinate.getZoom()); 457 | 458 | glm::dvec2 _coordinate(tileCoordinate.getColumn() - zoomedCenterCoordinate.getColumn(), 459 | tileCoordinate.getRow() - zoomedCenterCoordinate.getRow()); 460 | 461 | glm::dvec2 result = pixelCenter + (scaledTileSize * _coordinate); 462 | 463 | return result; 464 | } 465 | 466 | 467 | Geo::Coordinate MapTileLayer::pixelsToGeo(const glm::vec2& pixelCoordinate) const 468 | { 469 | return _tiles->provider()->tileToGeo(pixelsToTile(pixelCoordinate).getZoomedTo(_center.getZoom())); 470 | } 471 | 472 | 473 | glm::vec2 MapTileLayer::geoToPixels(const Geo::Coordinate& geoCoordinate) const 474 | { 475 | return tileToPixels(_tiles->provider()->geoToWorld(geoCoordinate).getZoomedTo(_center.getZoom())); 476 | } 477 | 478 | 479 | } } // namespace ofx::Maps 480 | -------------------------------------------------------------------------------- /libs/ofxMaps/src/MapTileProvider.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2014 Christopher Baker 3 | // 4 | // SPDX-License-Identifier: MIT 5 | // 6 | 7 | 8 | #include "ofx/Maps/MapTileProvider.h" 9 | #include "Poco/NumberFormatter.h" 10 | #include "Poco/String.h" 11 | #include "ofx/IO/Hash.h" 12 | #include "ofLog.h" 13 | #include "ofMath.h" 14 | #include "ofx/Maps/TileKey.h" 15 | #include "ofx/Maps/TileTemplate.h" 16 | #include "ofMath.h" 17 | 18 | 19 | namespace ofx { 20 | namespace Maps { 21 | 22 | 23 | const Geo::CoordinateBounds MapTileProvider::DEFAULT_BOUNDS = { Geo::Coordinate(-180, -90), Geo::Coordinate(180, 90) }; 24 | const TileCoordinate MapTileProvider::DEFAULT_CENTER = { 0, 0, 0 }; 25 | const SperhicalMercatorProjection MapTileProvider::DEFAULT_PROJECTION = SperhicalMercatorProjection(); 26 | 27 | 28 | MapTileProvider::MapTileProvider(): 29 | MapTileProvider(std::vector(), 30 | DEFAULT_MIN_ZOOM, 31 | DEFAULT_MAX_ZOOM, 32 | DEFAULT_TILE_WIDTH, 33 | DEFAULT_TILE_HEIGHT, 34 | DEFAULT_BOUNDS, 35 | DEFAULT_CENTER, 36 | DEFAULT_PROJECTION) 37 | { 38 | } 39 | 40 | 41 | 42 | MapTileProvider::MapTileProvider(const std::vector& URITemplates, 43 | int minZoom, 44 | int maxZoom, 45 | int tileWidth, 46 | int tileHeight, 47 | const Geo::CoordinateBounds& bounds, 48 | const TileCoordinate& center, 49 | const BaseProjection& projection): 50 | _minZoom(minZoom), 51 | _maxZoom(maxZoom), 52 | _tileSize(tileWidth, tileHeight), 53 | _bounds(bounds), 54 | _center(center), 55 | _projection(projection) 56 | { 57 | _setURITemplates(URITemplates); 58 | } 59 | 60 | 61 | MapTileProvider::~MapTileProvider() 62 | { 63 | } 64 | 65 | 66 | std::string MapTileProvider::id() const 67 | { 68 | return _id; 69 | } 70 | 71 | 72 | std::string MapTileProvider::name() const 73 | { 74 | return _name; 75 | } 76 | 77 | 78 | std::string MapTileProvider::description() const 79 | { 80 | return _description; 81 | } 82 | 83 | 84 | std::string MapTileProvider::attribution() const 85 | { 86 | return _attribution; 87 | } 88 | 89 | 90 | std::string MapTileProvider::version() const 91 | { 92 | return _version; 93 | } 94 | 95 | 96 | int MapTileProvider::minZoom() const 97 | { 98 | return _minZoom; 99 | } 100 | 101 | 102 | int MapTileProvider::maxZoom() const 103 | { 104 | return _maxZoom; 105 | } 106 | 107 | 108 | float MapTileProvider::tileWidth() const 109 | { 110 | return _tileSize.x; 111 | } 112 | 113 | 114 | float MapTileProvider::tileHeight() const 115 | { 116 | return _tileSize.y; 117 | } 118 | 119 | 120 | glm::vec2 MapTileProvider::tileSize() const 121 | { 122 | return _tileSize; 123 | } 124 | 125 | 126 | Geo::CoordinateBounds MapTileProvider::bounds() const 127 | { 128 | return _bounds; 129 | } 130 | 131 | 132 | TileCoordinate MapTileProvider::center() const 133 | { 134 | return _center; 135 | } 136 | 137 | 138 | double MapTileProvider::zoomForScale(double scale) const 139 | { 140 | return std::log(scale) / glm::ln_two(); 141 | } 142 | 143 | 144 | TileCoordinate MapTileProvider::geoToWorld(const Geo::Coordinate& location) const 145 | { 146 | return _projection.geoToWorld(location); 147 | } 148 | 149 | 150 | Geo::Coordinate MapTileProvider::tileToGeo(const TileCoordinate& coordinate) const 151 | { 152 | return _projection.tileToGeo(coordinate); 153 | } 154 | 155 | 156 | std::string MapTileProvider::getTileURI(const TileKey& key) const 157 | { 158 | std::size_t index = static_cast(ofRandom(_URITemplates.size())); 159 | 160 | auto tileURI = _URITemplates[index]; 161 | auto templateParameters = _URITemplateParameters[index]; 162 | 163 | for (const auto& templateParameter: templateParameters) 164 | { 165 | std::string templateValue; 166 | 167 | if (getTileURITemplateValue(key, templateParameter, templateValue)) 168 | { 169 | Poco::replaceInPlace(tileURI, templateParameter, templateValue); 170 | } 171 | else 172 | { 173 | ofLogWarning("MapTileProvider::getTileURI") << "Ignoring unknown template parameter: " << templateParameter; 174 | } 175 | } 176 | 177 | return tileURI; 178 | } 179 | 180 | 181 | bool MapTileProvider::isCacheable() const 182 | { 183 | return true; 184 | } 185 | 186 | 187 | const std::vector MapTileProvider::URITemplates() const 188 | { 189 | return _URITemplates; 190 | } 191 | 192 | 193 | std::map MapTileProvider::dictionary() const 194 | { 195 | return _dictionary; 196 | } 197 | 198 | 199 | bool MapTileProvider::getTileURITemplateValue(const TileKey& key, 200 | const std::string& templateParameter, 201 | std::string& templateValue) const 202 | { 203 | if (templateParameter == TileTemplate::TEMPLATE_PARAM_X) 204 | { 205 | templateValue = Poco::NumberFormatter::format(static_cast(key.column())); 206 | return true; 207 | } 208 | else if (templateParameter == TileTemplate::TEMPLATE_PARAM_Y) 209 | { 210 | templateValue = Poco::NumberFormatter::format(static_cast(key.row())); 211 | return true; 212 | } 213 | if (templateParameter == TileTemplate::TEMPLATE_PARAM_ZOOM) 214 | { 215 | templateValue = Poco::NumberFormatter::format(static_cast(key.zoom())); 216 | return true; 217 | } 218 | else if (templateParameter == TileTemplate::TEMPLATE_PARAM_TILE_ID) 219 | { 220 | templateValue = key.tileId(); 221 | return true; 222 | } 223 | else if (templateParameter == TileTemplate::TEMPLATE_PARAM_SET_ID) 224 | { 225 | templateValue = key.setId(); 226 | return true; 227 | } 228 | else 229 | { 230 | if (templateParameter.length() > 1) 231 | { 232 | auto search = templateParameter.substr(1, templateParameter.length() - 2); 233 | 234 | auto iter = _dictionary.find(search); 235 | 236 | if (iter != _dictionary.end()) 237 | { 238 | templateValue = iter->second; 239 | return true; 240 | } 241 | } 242 | 243 | return false; 244 | } 245 | } 246 | 247 | 248 | void MapTileProvider::_setURITemplates(const std::vector& templates) 249 | { 250 | // Update parameters and generate ID. 251 | _URITemplates = templates; 252 | _URITemplateParameters.clear(); 253 | 254 | std::size_t hash = 0; 255 | 256 | for (auto _URITemplate: _URITemplates) 257 | { 258 | IO::Hash::combine(hash, _URITemplate); 259 | _URITemplateParameters.push_back(TileTemplate::extractTemplateParameters(_URITemplate)); 260 | } 261 | 262 | _id = std::to_string(hash); 263 | } 264 | 265 | 266 | MapTileProvider MapTileProvider::fromJSON(const ofJson& json) 267 | { 268 | MapTileProvider provider; 269 | 270 | auto iter = json.cbegin(); 271 | while (iter != json.cend()) 272 | { 273 | const auto& key = iter.key(); 274 | const auto& value = iter.value(); 275 | 276 | if (key == "tilejson") { } 277 | else if (key == "name") provider._name = value; 278 | else if (key == "description") provider._description = value; 279 | else if (key == "version") provider._version = value; 280 | else if (key == "attribution") provider._attribution = value; 281 | else if (key == "template") { ofLogWarning("MapTileProvider::fromJSON") << "Unsupported TileJSON field: " << key; } 282 | else if (key == "legend") { ofLogWarning("MapTileProvider::fromJSON") << "Unsupported TileJSON field: " << key; } 283 | else if (key == "scheme") { ofLogWarning("MapTileProvider::fromJSON") << "Unsupported TileJSON field: " << key; } 284 | else if (key == "tiles") 285 | { 286 | std::vector uriTemplates; 287 | for (const auto& uriTemplate: value) 288 | { 289 | uriTemplates.push_back(uriTemplate); 290 | } 291 | provider._setURITemplates(uriTemplates); 292 | } 293 | else if (key == "grids") { ofLogWarning("MapTileProvider::fromJSON") << "Unsupported TileJSON field: " << key; } 294 | else if (key == "data") { ofLogWarning("MapTileProvider::fromJSON") << "Unsupported TileJSON field: " << key; } 295 | else if (key == "minzoom") provider._minZoom = value; 296 | else if (key == "maxzoom") provider._maxZoom = value; 297 | else if (key == "tilewidth") provider._tileSize.x = value.get(); 298 | else if (key == "tileheight") provider._tileSize.y = value.get(); 299 | else if (key == "bounds") 300 | { 301 | provider._bounds = Geo::CoordinateBounds(Geo::Coordinate(value[0], value[1]), 302 | Geo::Coordinate(value[2], value[3])); 303 | } 304 | else if (key == "center") 305 | { 306 | auto center = provider.geoToWorld(Geo::Coordinate(value[0], value[1])); 307 | center.setZoom(value[2]); 308 | provider._center = center; 309 | } 310 | else 311 | { 312 | provider._dictionary[key] = value; 313 | } 314 | 315 | ++iter; 316 | } 317 | 318 | return provider; 319 | } 320 | 321 | 322 | ofJson MapTileProvider::toJSON(const MapTileProvider& provider) 323 | { 324 | ofJson json; 325 | 326 | json["tilejson"] = "2.1.0"; 327 | 328 | if (!provider.name().empty()) json["name"] = provider.name(); 329 | if (!provider.description().empty()) json["description"] = provider.description(); 330 | if (!provider.version().empty()) json["version"] = provider.version(); 331 | if (!provider.attribution().empty()) json["attribution"] = provider.attribution(); 332 | // if (!provider.template().empty()) json["template"] = provider.template(); 333 | // if (!provider.legend().empty()) json["legend"] = provider.legend(); 334 | // if (!provider.scheme().empty()) json["scheme"] = provider.scheme(); 335 | if (!provider.attribution().empty()) json["tiles"] = provider.URITemplates(); 336 | // if (!provider.grids().empty()) json["grids"] = provider.grids(); 337 | // if (!provider.data().empty()) json["data"] = provider.data(); 338 | 339 | json["minzoom"] = provider.minZoom(); 340 | json["maxzoom"] = provider.maxZoom(); 341 | 342 | json["tilewidth"] = provider.tileWidth(); 343 | json["tileheight"] = provider.tileHeight(); 344 | 345 | if (!provider.maxZoom()) json["maxzoom"] = provider.maxZoom(); 346 | 347 | json["bounds"] = { provider.bounds().northwest().getLatitude(), 348 | provider.bounds().northwest().getLongitude(), 349 | provider.bounds().southeast().getLatitude(), 350 | provider.bounds().southeast().getLongitude() 351 | }; 352 | 353 | auto centerGeo = provider.tileToGeo(provider.center()); 354 | 355 | 356 | json["center"] = { centerGeo.getLatitude(), 357 | centerGeo.getLongitude(), 358 | provider.center().getZoom() 359 | }; 360 | 361 | for (const auto& entry: provider._dictionary) 362 | { 363 | json[entry.first] = entry.second; 364 | } 365 | 366 | return json; 367 | } 368 | 369 | 370 | 371 | } } // namespace ofx::Maps 372 | -------------------------------------------------------------------------------- /libs/ofxMaps/src/MapTileSet.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2014 Christopher Baker 3 | // 4 | // SPDX-License-Identifier: MIT 5 | // 6 | 7 | 8 | #include "ofx/Maps/MapTileSet.h" 9 | #include "Poco/Net/HTTPResponse.h" 10 | #include "Poco/Net/MediaType.h" 11 | #include "ofx/HTTP/Client.h" 12 | #include "ofx/HTTP/GetRequest.h" 13 | #include "ofx/Maps/MBTilesCache.h" 14 | 15 | 16 | namespace ofx { 17 | namespace Maps { 18 | 19 | 20 | const std::string MapTileSet::DEFAULT_BUFFER_CACHE_LOCATION = "cache"; 21 | 22 | 23 | MapTileSet::MapTileSet(std::size_t cacheSize, 24 | std::shared_ptr provider, 25 | std::shared_ptr bufferCache): 26 | // Cache::BaseResourceCache(cacheSize, taskQueue), 27 | _provider(provider), 28 | _bufferCache(bufferCache), 29 | _onAddListener(this->onAdd.newListener(this, &MapTileSet::_onAdd)) 30 | { 31 | if (_bufferCache == nullptr && _provider->isCacheable()) 32 | { 33 | _bufferCache = std::make_shared(*_provider, DEFAULT_BUFFER_CACHE_LOCATION); 34 | } 35 | } 36 | 37 | 38 | std::shared_ptr MapTileSet::load(Cache::CacheRequestTask& task) 39 | { 40 | std::shared_ptr buffer = _tryLoadFromCache(task); 41 | 42 | bool isCached = (buffer != nullptr); 43 | 44 | if (!isCached) 45 | { 46 | buffer = _tryLoadFromURI(task); 47 | } 48 | 49 | if (buffer != nullptr) 50 | { 51 | ofPixels pixels; 52 | 53 | if (!ofLoadImage(pixels, *buffer)) 54 | { 55 | ofLogError("TileStore::load") << "Failure to load pixels."; 56 | return nullptr; 57 | } 58 | else if (!isCached && _bufferCache != nullptr && _provider->isCacheable()) 59 | { 60 | _bufferCache->add(task.key(), buffer); 61 | } 62 | 63 | return std::make_shared(pixels); 64 | } 65 | else return nullptr; 66 | } 67 | 68 | 69 | std::string MapTileSet::toTaskId(const TileKey& key) const 70 | { 71 | // Create a unique task id. 72 | return _provider->id() + "_" + key.toString(); 73 | } 74 | 75 | 76 | std::shared_ptr MapTileSet::_tryLoadFromCache(Cache::CacheRequestTask& task) 77 | { 78 | if (_bufferCache != nullptr) 79 | { 80 | return _bufferCache->get(task.key()); 81 | } 82 | else return nullptr; 83 | } 84 | 85 | 86 | std::shared_ptr MapTileSet::provider() const 87 | { 88 | return _provider; 89 | } 90 | 91 | 92 | std::shared_ptr MapTileSet::_tryLoadFromURI(Cache::CacheRequestTask& task) 93 | { 94 | std::shared_ptr buffer = nullptr; 95 | 96 | // Launch a thread to go get it! 97 | Poco::URI uri = Poco::URI(_provider->getTileURI(task.key())); 98 | 99 | if (uri.getScheme() == "http" || uri.getScheme() == "https") 100 | { 101 | HTTP::Client client; 102 | HTTP::Context context; 103 | HTTP::GetRequest request(uri.toString()); 104 | 105 | auto listener = context.events.onHTTPClientResponseProgress.newListener([&task](HTTP::ClientResponseProgressEventArgs& args) 106 | { 107 | task.setProgress(args.progress().progress()); 108 | }); 109 | 110 | auto response = client.execute(context, request); 111 | 112 | if (response->getStatus() == Poco::Net::HTTPResponse::HTTP_OK) 113 | { 114 | Poco::Net::MediaType mediaType(response->getContentType()); 115 | 116 | if (mediaType.matches("image")) 117 | { 118 | buffer = std::make_shared(response->stream()); 119 | } 120 | else 121 | { 122 | ofLogError("TileStore::_tryLoadFromURI") << "Unsupported media type: " << mediaType.toString(); 123 | } 124 | } 125 | else 126 | { 127 | ofLogError("TileStore::_tryLoadFromURI") << "Invalid response: " << response->getStatus() << ": " << response->getReason() << ": " << uri.toString(); 128 | 129 | buffer = std::make_shared(response->stream()); 130 | std::cout << buffer->getText() << std::endl; 131 | 132 | } 133 | } 134 | else 135 | { 136 | ofLogError("TileStore::_tryLoadFromURI") << "Unsupported URI scheme: " << uri.toString(); 137 | } 138 | 139 | return buffer; 140 | } 141 | 142 | 143 | void MapTileSet::_onAdd(const std::pair>& args) 144 | { 145 | // We get a callback when it's cached (in the main thread), so we load it. 146 | args.second->loadTexture(); 147 | } 148 | 149 | 150 | } } // namespace ofx::Maps 151 | -------------------------------------------------------------------------------- /libs/ofxMaps/src/SphericalMercatorProjection.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2014 Christopher Baker 3 | // Copyright (c) -2014 Tom Carden 4 | // 5 | // SPDX-License-Identifier: MIT 6 | // 7 | 8 | 9 | #include "ofx/Maps/SphericalMercatorProjection.h" 10 | 11 | 12 | namespace ofx { 13 | namespace Maps { 14 | 15 | 16 | const std::string SperhicalMercatorProjection::EPSG_3857 = "EPSG:3857"; 17 | 18 | 19 | SperhicalMercatorProjection::SperhicalMercatorProjection(): 20 | BaseProjection(EPSG_3857, 21 | DEFAULT_ZOOM, 22 | Transformation(-glm::pi(), glm::pi(), 0, 0, 23 | glm::pi(), glm::pi(), 1, 0, 24 | -glm::pi(), -glm::pi(), 0, 1)) 25 | { 26 | } 27 | 28 | 29 | SperhicalMercatorProjection::~SperhicalMercatorProjection() 30 | { 31 | } 32 | 33 | 34 | glm::dvec2 SperhicalMercatorProjection::rawProject(const glm::dvec2& point) const 35 | { 36 | return glm::dvec2(point.x, 37 | std::log(std::tan(glm::quarter_pi() + 0.5 * point.y))); 38 | } 39 | 40 | 41 | glm::dvec2 SperhicalMercatorProjection::rawUnproject(const glm::dvec2& point) const 42 | { 43 | return glm::dvec2(point.x, 44 | 2.0 * std::atan(std::pow(glm::e(), 1.0 * point.y)) - glm::half_pi()); 45 | } 46 | 47 | 48 | } } // namespace ofx::Maps 49 | -------------------------------------------------------------------------------- /libs/ofxMaps/src/Tile.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2014 Christopher Baker 3 | // 4 | // SPDX-License-Identifier: MIT 5 | // 6 | 7 | 8 | #include "ofx/Maps/Tile.h" 9 | 10 | 11 | namespace ofx { 12 | namespace Maps { 13 | 14 | 15 | Tile::Tile(): 16 | _type(Type::EMPTY) 17 | { 18 | } 19 | 20 | 21 | Tile::Tile(const ofPixels& pixels): 22 | _type(Type::RASTER), 23 | _pixels(pixels) 24 | { 25 | } 26 | 27 | 28 | Tile::~Tile() 29 | { 30 | } 31 | 32 | 33 | void Tile::draw(float x, float y, float width, float height) const 34 | { 35 | _texture.draw(x, y, width, height); 36 | } 37 | 38 | 39 | float Tile::getWidth() const 40 | { 41 | return _pixels.getWidth(); 42 | } 43 | 44 | 45 | float Tile::getHeight() const 46 | { 47 | return _pixels.getHeight(); 48 | } 49 | 50 | 51 | bool Tile::empty() const 52 | { 53 | return _type == Type::EMPTY; 54 | } 55 | 56 | 57 | const ofPixels& Tile::pixels() const 58 | { 59 | return _pixels; 60 | } 61 | 62 | 63 | const ofTexture& Tile::texture() const 64 | { 65 | return _texture; 66 | } 67 | 68 | 69 | Tile::Type Tile::type() const 70 | { 71 | return _type; 72 | } 73 | 74 | 75 | bool Tile::hasTexture() const 76 | { 77 | return _texture.isAllocated(); 78 | } 79 | 80 | 81 | void Tile::loadTexture() 82 | { 83 | _texture.loadData(_pixels); 84 | } 85 | 86 | 87 | void Tile::clearTexture() 88 | { 89 | _texture.clear(); 90 | } 91 | 92 | 93 | } } // namespace ofx::Maps 94 | -------------------------------------------------------------------------------- /libs/ofxMaps/src/TileCoordinate.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2014 Christopher Baker 3 | // Copyright (c) -2014 Tom Carden 4 | // 5 | // SPDX-License-Identifier: MIT 6 | // 7 | 8 | 9 | #include "ofx/Maps/TileCoordinate.h" 10 | #include "Poco/Exception.h" 11 | #include "ofMath.h" 12 | #include "ofVectorMath.h" 13 | #include "ofx/IO/Hash.h" 14 | #include "glm/common.hpp" 15 | #include 16 | 17 | namespace ofx { 18 | namespace Maps { 19 | 20 | 21 | TileCoordinate::TileCoordinate(): TileCoordinate(0, 0, 0) 22 | { 23 | } 24 | 25 | 26 | //TileCoordinate::TileCoordinate(const glm::dvec2& columnRow, double zoom): 27 | // TileCoordinate(columnRow.x, columnRow.y, zoom) 28 | //{ 29 | //} 30 | 31 | 32 | TileCoordinate::TileCoordinate(double column, 33 | double row, 34 | double zoom): 35 | _column(column), 36 | _row(row), 37 | _zoom(zoom) 38 | { 39 | } 40 | 41 | 42 | void TileCoordinate::setColumn(double column) 43 | { 44 | _column = column; 45 | } 46 | 47 | 48 | double TileCoordinate::getColumn() const 49 | { 50 | return _column; 51 | } 52 | 53 | 54 | double TileCoordinate::getNormalizedColumn() const 55 | { 56 | double scale = getScaleForZoom(_zoom); 57 | 58 | if (_column < 0 || _column > scale) 59 | { 60 | return _column - scale * std::floor(_column / scale); 61 | } 62 | 63 | return _column; 64 | } 65 | 66 | 67 | 68 | int64_t TileCoordinate::getFlooredColumn() const 69 | { 70 | return static_cast(std::floor(getNormalizedColumn())); 71 | } 72 | 73 | 74 | void TileCoordinate::setRow(double row) 75 | { 76 | _row = row; 77 | } 78 | 79 | 80 | double TileCoordinate::getRow() const 81 | { 82 | return _row; 83 | } 84 | 85 | 86 | double TileCoordinate::getNormalizedRow() const 87 | { 88 | double scale = getScaleForZoom(_zoom); 89 | 90 | if (_row < 0 || _row > scale) 91 | { 92 | return _row - scale * std::floor(_row / scale); 93 | } 94 | 95 | return _row; 96 | } 97 | 98 | 99 | int64_t TileCoordinate::getFlooredRow() const 100 | { 101 | return static_cast(std::floor(getNormalizedRow())); 102 | } 103 | 104 | 105 | void TileCoordinate::setZoom(double zoom) 106 | { 107 | _zoom = zoom; 108 | } 109 | 110 | 111 | double TileCoordinate::getZoom() const 112 | { 113 | return _zoom; 114 | } 115 | 116 | 117 | int64_t TileCoordinate::getFlooredZoom() const 118 | { 119 | return static_cast(std::floor(_zoom)); 120 | } 121 | 122 | 123 | double TileCoordinate::scaleForZoom() const 124 | { 125 | return getScaleForZoom(_zoom); 126 | } 127 | 128 | 129 | void TileCoordinate::zoomTo(double zoom) 130 | { 131 | auto scale = getScaleForZoom(zoom - _zoom); 132 | _column *= scale; 133 | _row *= scale; 134 | _zoom = zoom; 135 | } 136 | 137 | 138 | TileCoordinate TileCoordinate::getZoomedTo(double zoom) const 139 | { 140 | TileCoordinate result = *this; 141 | result.zoomTo(zoom); 142 | return result; 143 | } 144 | 145 | 146 | void TileCoordinate::zoomBy(double zoom) 147 | { 148 | auto scale = getScaleForZoom(zoom); 149 | _column *= scale; 150 | _row *= scale; 151 | _zoom += zoom; 152 | } 153 | 154 | 155 | TileCoordinate TileCoordinate::getZoomedBy(double zoom) const 156 | { 157 | TileCoordinate result = *this; 158 | result.zoomBy(zoom); 159 | return result; 160 | } 161 | 162 | 163 | TileCoordinate TileCoordinate::moveRightBy(double distance) 164 | { 165 | _column += distance; 166 | return *this; 167 | } 168 | 169 | 170 | TileCoordinate TileCoordinate::moveLeftBy(double distance) 171 | { 172 | _column -= distance; 173 | return *this; 174 | } 175 | 176 | 177 | TileCoordinate TileCoordinate::moveUpBy(double distance) 178 | { 179 | _row -= distance; 180 | return *this; 181 | } 182 | 183 | TileCoordinate TileCoordinate::moveDownBy(double distance) 184 | { 185 | _row += distance; 186 | return *this; 187 | } 188 | 189 | 190 | TileCoordinate TileCoordinate::getNeighbor(double columnDistance, 191 | double rowDistance) const 192 | { 193 | return TileCoordinate(_column + columnDistance, 194 | _row + rowDistance, 195 | _zoom); 196 | } 197 | 198 | 199 | TileCoordinate TileCoordinate::getNeighborRight() const 200 | { 201 | return getNeighbor(1, 0); 202 | } 203 | 204 | 205 | TileCoordinate TileCoordinate::getNeighborLeft() const 206 | { 207 | return getNeighbor(-1, 0); 208 | } 209 | 210 | 211 | TileCoordinate TileCoordinate::getNeighborUp() const 212 | { 213 | return getNeighbor(0, -1); 214 | } 215 | 216 | 217 | TileCoordinate TileCoordinate::getNeighborDown() const 218 | { 219 | return getNeighbor(0, 1); 220 | } 221 | 222 | 223 | TileCoordinate TileCoordinate::getNormalizedTileCoordinate() const 224 | { 225 | TileCoordinate coordinate = *this; 226 | // TODO if this zoom is negative, these while loops could get stuck. 227 | 228 | if (coordinate.getZoom() < 0) 229 | { 230 | throw Poco::InvalidArgumentException("Zoom must be >= 0"); 231 | } 232 | 233 | int gridSize = getScaleForZoom(coordinate.getZoom()); 234 | 235 | double wrappedColumn = std::fmod(coordinate.getColumn(), gridSize); 236 | 237 | while (wrappedColumn < 0) 238 | { 239 | wrappedColumn += gridSize; 240 | } 241 | 242 | double wrappedRow = std::fmod(coordinate.getRow(), gridSize); 243 | 244 | while (wrappedRow < 0) 245 | { 246 | wrappedRow += gridSize; 247 | } 248 | 249 | return TileCoordinate(wrappedColumn, 250 | wrappedRow, 251 | coordinate.getZoom()); 252 | } 253 | 254 | 255 | //TileCoordinate TileCoordinate::getFlooredRowAndColumn() const 256 | //{ 257 | // return TileCoordinate(getFlooredColumn(), 258 | // getFlooredRow(), 259 | // getZoom()); 260 | //} 261 | 262 | 263 | TileCoordinate TileCoordinate::getClampedRowAndColumn() const 264 | { 265 | double gridSize = getScaleForZoom(getZoom()) - 1; 266 | 267 | return TileCoordinate(glm::clamp(getColumn(), 0.0, gridSize), 268 | glm::clamp(getRow(), 0.0, gridSize), 269 | getZoom()); 270 | } 271 | 272 | 273 | double TileCoordinate::getScaleForZoom(int zoom) 274 | { 275 | return std::pow(2.0, zoom); 276 | } 277 | 278 | 279 | bool TileCoordinate::operator < (const TileCoordinate& coordinate) const 280 | { 281 | bool areZoomsEqual = ofIsFloatEqual(_zoom, coordinate._zoom); 282 | 283 | return (_zoom < coordinate._zoom) 284 | || (areZoomsEqual && _row < coordinate._row) 285 | || (areZoomsEqual && ofIsFloatEqual(_row, coordinate._row) && _column < coordinate._column); 286 | } 287 | 288 | 289 | bool TileCoordinate::operator == (const TileCoordinate& coordinate) const 290 | { 291 | return ofIsFloatEqual(_column, coordinate.getColumn()) 292 | && ofIsFloatEqual(_row, coordinate.getRow()) 293 | && ofIsFloatEqual(_zoom, coordinate.getZoom()); 294 | } 295 | 296 | 297 | TileCoordinate& TileCoordinate::operator = (const TileCoordinate& coordinate) 298 | { 299 | _column = coordinate._column; 300 | _row = coordinate._row; 301 | _zoom = coordinate._zoom; 302 | return *this; 303 | } 304 | 305 | 306 | std::string TileCoordinate::toString(int precision) const 307 | { 308 | std::stringstream ss; 309 | ss << std::fixed << std::setprecision(precision) << _column << ","; 310 | ss << std::fixed << std::setprecision(precision) << _row << ","; 311 | ss << std::fixed << std::setprecision(precision) << _zoom; 312 | return ss.str(); 313 | } 314 | 315 | 316 | std::size_t TileCoordinate::hash() const 317 | { 318 | std::size_t seed = 0; 319 | IO::Hash::combine(seed, _column); 320 | IO::Hash::combine(seed, _row); 321 | IO::Hash::combine(seed, _zoom); 322 | return seed; 323 | } 324 | 325 | 326 | } } // namespace ofx::Maps 327 | -------------------------------------------------------------------------------- /libs/ofxMaps/src/TileKey.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2014 Christopher Baker 3 | // 4 | // SPDX-License-Identifier: MIT 5 | // 6 | 7 | 8 | #include "ofx/Maps/TileKey.h" 9 | #include 10 | #include "ofx/IO/Hash.h" 11 | 12 | 13 | namespace ofx { 14 | namespace Maps { 15 | 16 | 17 | const std::string TileKey::DEFAULT_TILE_ID = ""; 18 | const std::string TileKey::DEFAULT_SET_ID = ""; 19 | 20 | 21 | TileKey::TileKey() 22 | { 23 | } 24 | 25 | 26 | TileKey::TileKey(const TileKey& key): 27 | TileKey(key.column(), 28 | key.row(), 29 | key.zoom(), 30 | // key.providerId(), 31 | key.setId(), 32 | key.tileId()) 33 | { 34 | } 35 | 36 | 37 | TileKey::TileKey(int64_t column, 38 | int64_t row, 39 | int64_t zoom, 40 | // const std::string& providerId, 41 | const std::string& setId, 42 | const std::string& tileId): 43 | _column(column), 44 | _row(row), 45 | _zoom(zoom), 46 | // _providerId(providerId), 47 | _setId(setId), 48 | _tileId(tileId) 49 | { 50 | } 51 | 52 | 53 | TileKey::~TileKey() 54 | { 55 | } 56 | 57 | 58 | int64_t TileKey::column() const 59 | { 60 | return _column; 61 | } 62 | 63 | 64 | int64_t TileKey::row() const 65 | { 66 | return _row; 67 | } 68 | 69 | 70 | int64_t TileKey::zoom() const 71 | { 72 | return _zoom; 73 | } 74 | 75 | 76 | //std::string TileKey::providerId() const 77 | //{ 78 | // return _providerId; 79 | //} 80 | 81 | 82 | std::string TileKey::tileId() const 83 | { 84 | return _tileId; 85 | } 86 | 87 | 88 | std::string TileKey::setId() const 89 | { 90 | return _setId; 91 | } 92 | 93 | 94 | std::string TileKey::toString() const 95 | { 96 | std::stringstream ss; 97 | ss << _column << ","; 98 | ss << _row << ","; 99 | ss << _zoom << ","; 100 | ss << _tileId << ","; 101 | ss << _setId; 102 | return ss.str(); 103 | } 104 | 105 | 106 | std::size_t TileKey::hash() const 107 | { 108 | std::size_t seed = 0; 109 | IO::Hash::combine(seed, _column); 110 | IO::Hash::combine(seed, _row); 111 | IO::Hash::combine(seed, _zoom); 112 | // IO::Hash::combine(hash, _providerId); 113 | IO::Hash::combine(seed, _tileId); 114 | IO::Hash::combine(seed, _setId); 115 | return seed; 116 | } 117 | 118 | 119 | 120 | 121 | bool TileKey::operator < (const TileKey& key) const 122 | { 123 | /* 124 | if (_providerId != key.providerId()) 125 | { 126 | return _providerId < key.providerId(); 127 | } 128 | else */if (_setId != key.setId()) 129 | { 130 | return _setId < key.setId(); 131 | } 132 | else if (_tileId != key.tileId()) 133 | { 134 | return _tileId < key.tileId(); 135 | } 136 | else if (_row != key.row()) 137 | { 138 | return _row < key.row(); 139 | } 140 | else if (_column != key.column()) 141 | { 142 | return _column < key.column(); 143 | } 144 | else 145 | { 146 | return _zoom < key.zoom(); 147 | } 148 | } 149 | 150 | 151 | } } // namespace ofx::Maps 152 | -------------------------------------------------------------------------------- /libs/ofxMaps/src/TileTemplate.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2014 Christopher Baker 3 | // 4 | // SPDX-License-Identifier: MIT 5 | // 6 | 7 | 8 | #include "ofx/Maps/TileTemplate.h" 9 | #include 10 | #include 11 | 12 | 13 | namespace ofx { 14 | namespace Maps { 15 | 16 | 17 | const Poco::RegularExpression TileTemplate::TEMPLATE_PARAM_REGEX("{[a-zA-Z0-9_-]+}"); 18 | 19 | const std::string TileTemplate::TEMPLATE_PARAM_QUADKEY = "{quadkey}"; 20 | const std::string TileTemplate::TEMPLATE_PARAM_ZOOM = "{z}"; 21 | const std::string TileTemplate::TEMPLATE_PARAM_X = "{x}"; 22 | const std::string TileTemplate::TEMPLATE_PARAM_Y = "{y}"; 23 | const std::string TileTemplate::TEMPLATE_PARAM_TILE_ID = "{tile_id}"; 24 | const std::string TileTemplate::TEMPLATE_PARAM_SET_ID = "{set_id}"; 25 | 26 | 27 | std::string TileTemplate::tileCoordinateToQuadKey(int tileX, 28 | int tileY, 29 | int levelOfDetail) 30 | { 31 | std::stringstream quadKey; 32 | 33 | for (int i = levelOfDetail; i > 0; --i) 34 | { 35 | char digit = '0'; 36 | 37 | int mask = 1 << (i - 1); 38 | 39 | if ((tileX & mask) != 0) 40 | { 41 | digit++; 42 | } 43 | 44 | if ((tileY & mask) != 0) 45 | { 46 | digit++; 47 | digit++; 48 | } 49 | 50 | quadKey << digit; 51 | } 52 | 53 | return quadKey.str(); 54 | } 55 | 56 | 57 | bool TileTemplate::quadKeyToTileCoordinate(const std::string& quadKey, 58 | int& tileX, 59 | int& tileY, 60 | int& levelOfDetail) 61 | { 62 | tileX = 0; 63 | tileY = 0; 64 | 65 | levelOfDetail = quadKey.length(); 66 | 67 | for (int i = levelOfDetail; i > 0; --i) 68 | { 69 | int mask = 1 << (i - 1); 70 | 71 | switch (quadKey[levelOfDetail - i]) 72 | { 73 | case '0': 74 | break; 75 | 76 | case '1': 77 | tileX |= mask; 78 | break; 79 | 80 | case '2': 81 | tileY |= mask; 82 | break; 83 | 84 | case '3': 85 | tileX |= mask; 86 | tileY |= mask; 87 | break; 88 | 89 | default: 90 | return false; 91 | } 92 | } 93 | 94 | return true; 95 | } 96 | 97 | 98 | std::vector TileTemplate::extractTemplateParameters(const std::string& URITemplate) 99 | { 100 | std::vector URITemplateParameters; 101 | 102 | Poco::RegularExpression::Match match; 103 | 104 | std::string::size_type offset = 0; 105 | 106 | while (TEMPLATE_PARAM_REGEX.match(URITemplate, offset, match)) 107 | { 108 | offset = match.offset + match.length; 109 | 110 | std::string parameter = std::string(URITemplate, match.offset, match.length); 111 | 112 | URITemplateParameters.push_back(parameter); 113 | } 114 | 115 | return URITemplateParameters; 116 | } 117 | 118 | 119 | } } // namespace ofx::Maps 120 | -------------------------------------------------------------------------------- /scripts/bootstrap.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | # Here we assume that the bootstrap script is in the ADDON_ROOT/scripts directory. 5 | pushd ${ADDON_ROOT:-$(cd "$( dirname "${BASH_SOURCE[0]}" )/.." && pwd)} > /dev/null 6 | git submodule update --init --recursive 7 | /usr/bin/env bash scripts/ofx/ofx bootstrap 8 | popd > /dev/null 9 | -------------------------------------------------------------------------------- /scripts/ci/install.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | OF_ROOT=$( cd "$(dirname "$0")/../../../.." ; pwd -P ) 5 | 6 | # Default addon github info. 7 | GH_USERNAME='bakercp' 8 | GH_BRANCH='master' 9 | GH_DEPTH=1 10 | 11 | # An array of required addons that will be gathered below. 12 | REQUIRED_ADDONS=() 13 | 14 | # Extract ADDON_DEPENDENCIES from addon_config.mk file. 15 | if [ -f ${OF_ROOT}/addons/${OF_ADDON_NAME}/addon_config.mk ]; then 16 | while read line; do 17 | if [[ $line == ADDON_DEPENDENCIES* ]] ; 18 | then 19 | line=${line#*=} 20 | IFS=' ' read -ra ADDR <<< "$line" 21 | for i in "${ADDR[@]}"; do 22 | REQUIRED_ADDONS+=($i) 23 | done 24 | fi 25 | done < ${OF_ROOT}/addons/${OF_ADDON_NAME}/addon_config.mk 26 | fi 27 | 28 | # Gather addons from all examples. 29 | for addons_make in ${OF_ROOT}/addons/${OF_ADDON_NAME}/example*/addons.make; do 30 | while read addon; do 31 | if [ ${addon} != ${OF_ADDON_NAME} ] ; 32 | then 33 | REQUIRED_ADDONS+=($addon) 34 | fi 35 | done < $addons_make 36 | done 37 | 38 | # We aren't de-duplicating array to keep it pure bash. 39 | for addon in "${REQUIRED_ADDONS[@]}" 40 | do 41 | if [ ! -d ${OF_ROOT}/addons/${addon} ]; then 42 | git clone --depth=$GH_DEPTH https://github.com/$GH_USERNAME/$addon.git ${OF_ROOT}/addons/${addon} 43 | fi 44 | done 45 | -------------------------------------------------------------------------------- /src/ofxMaps.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2014 Christopher Baker 3 | // 4 | // SPDX-License-Identifier: MIT 5 | // 6 | 7 | 8 | #pragma once 9 | 10 | 11 | #include "ofxCache.h" 12 | #include "ofxGeo.h" 13 | #include "ofxHTTP.h" 14 | #include "ofx/Maps/MapTileLayer.h" 15 | #include "ofx/Maps/MapTileProvider.h" 16 | #include "ofx/Maps/MapTileSet.h" 17 | #include "ofx/Maps/MBTilesCache.h" 18 | #include "ofx/Maps/Tile.h" 19 | #include "ofx/Maps/TileKey.h" 20 | 21 | 22 | namespace ofxMaps = ofx::Maps; 23 | --------------------------------------------------------------------------------