diff --git a/client/src/js/leaflet-tileLayer-pouchdb-cached.js b/client/src/js/leaflet-tileLayer-pouchdb-cached.js index fc032dd..9184b83 100644 --- a/client/src/js/leaflet-tileLayer-pouchdb-cached.js +++ b/client/src/js/leaflet-tileLayer-pouchdb-cached.js @@ -287,8 +287,36 @@ L.TileLayer.include({ this._seedOneTile(tile, remaining, seedData); } }.bind(this)); - } + }, + + cacheAhead: function(coords, done, zoom=18) { + var tile = document.createElement('img'); + + tile.onerror = L.bind(this._tileOnError, this, done, tile); + + if (this.options.crossOrigin) { + tile.crossOrigin = ''; + } + /* + Alt tag is *set to empty string to keep screen readers from reading URL and for compliance reasons + http://www.w3.org/TR/WCAG20-TECHS/H67 + */ + tile.alt = ''; + + let tileUrl = this.getTileUrl(coords); + tileUrl = tileUrl.replace('NaN', zoom); // fix tileUrl with correct zoom value + + // if available get cached tile image + this._db.get(tileUrl, + { + rev: true, + attachments: true, + binary: true, // return attachment data as Blobs instead of as base64-encoded strings + }, + this._onCacheLookup(tile, tileUrl, done) + ); + } }); export default L; diff --git a/client/src/js/lrm-google.js b/client/src/js/lrm-google.js index cf842cb..6d89230 100644 --- a/client/src/js/lrm-google.js +++ b/client/src/js/lrm-google.js @@ -3,6 +3,7 @@ const L = require('leaflet'); const decodePolyline = require('decode-google-map-polyline'); // app module imports +const { latLngToPoint, pointToLatLng } = require('./osm-tile-name'); const { makeRequest, BASE_ENDPOINTS } = require('./server-requests-utils') L.Routing = L.Routing || {}; @@ -12,8 +13,9 @@ L.Routing.Google = L.Class.extend({ }, - initialize: function(options) { + initialize: function(options, TILE_LAYER) { L.Util.setOptions(this, options); + this._TILE_LAYER = TILE_LAYER; }, route: function(waypoints, callback, context, options) { @@ -65,6 +67,18 @@ L.Routing.Google = L.Class.extend({ }); }); + // prefetch/cache polylines + if (this._TILE_LAYER) { + route.coordinates.forEach((latLng) => { + const coord = latLngToPoint(latLng.lat, latLng.lng, 18); + + // TODO - if needed cache all four corners by looping +1 for x/y + this._TILE_LAYER.cacheAhead(coord, () => { + // console.log('cached coord: ', coord); + }); + }); + } + routes.push(route); }); @@ -72,6 +86,6 @@ L.Routing.Google = L.Class.extend({ } }); -L.Routing.google = function(options={}) { - return new L.Routing.Google(options); +L.Routing.google = function(options={}, TILE_LAYER=null) { + return new L.Routing.Google(options, TILE_LAYER); }; diff --git a/client/src/js/osm-tile-name.js b/client/src/js/osm-tile-name.js new file mode 100644 index 0000000..b146ca5 --- /dev/null +++ b/client/src/js/osm-tile-name.js @@ -0,0 +1,51 @@ +/** + * Module for calculating tile name for slippy maps. + * + * Source: https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames + * + * Notes: + * This article describes the file naming conventions for the Slippy Map application. + * - Tiles are 256 × 256 pixel PNG files + * - Each zoom level is a directory, each column is a subdirectory, and each tile in that column is a file + * - Filename(url) format is /zoom/x/y.png + * The slippy map expects tiles to be served up at URLs following this scheme, so all tile server URLs look pretty similar. + */ + + // utility functions + const toRadians = (angle) => (angle * (Math.PI / 180)); + const toDegrees = (radian) => (radian * (180/ Math.PI)); + const getN = (zoom) => (Math.pow(2.0, zoom)); + const getSec = (radian) => (1 / Math.cos(radian)); + + /** + * Calculate x and y coords for tile url. + * + * @param {number} lat latitude in degrees + * @param {number} lng longitude in degrees + * @param {number} zoom zoom level + */ + exports.latLngToPoint = (lat, lng, zoom) => { + const n = getN(zoom); + const latRadians = toRadians(lat); + + const x = Math.floor(n * ((lng + 180) / 360)); + + const secLat = getSec(latRadians); + const y = Math.floor(n * (1 - (Math.log(Math.tan(latRadians) + secLat) / Math.PI)) / 2); + return { x, y, z: zoom }; + }; + + /** + * Estimate latitude and longitude for give tile url point. + * + * @param {number} x point x + * @param {number} y point y + * @param {number} zoom zoom level + */ +exports.pointToLatLng = (x, y, zoom) => { + const n = getN(zoom); + + const lng = (x / n) * 360 - 180; + const lat = Math.atan(Math.sinh(Math.PI - (y / n) * 2 * Math.PI)) * (180 / Math.PI); + return { lat, lng }; +}; diff --git a/client/src/routes/directions/index.js b/client/src/routes/directions/index.js index 90be88b..457bcd0 100644 --- a/client/src/routes/directions/index.js +++ b/client/src/routes/directions/index.js @@ -77,7 +77,7 @@ export default class Directions extends Component { showAlternatives: true, show: false, collapsible: false, - router: L.Routing.google(), + router: L.Routing.google({}, OSM_TILE_LAYER), }).addTo(map); this.control.on('routeselected', (e) => {