allartk / leaflet.offline Goto Github PK
View Code? Open in Web Editor NEWLeaflet offline layer
Home Page: https://allartk.github.io/leaflet.offline/
License: GNU Lesser General Public License v3.0
Leaflet offline layer
Home Page: https://allartk.github.io/leaflet.offline/
License: GNU Lesser General Public License v3.0
This library looks extremely promising but I cannot seem to follow the instructions in the read-me on how install it. There is no file 'dist/leaflet.offline.min.js'. I am by no means experienced in the Javascript world so I would be grateful for some advice. Thanks in advance!
At the same place there is mention of localforage which I suspect should instead be idb as mentioned under the features in version 2.
Is there a way to use this control programmatically? I find it tedious to have user having to click the save button for who know how many times.
To be honest, the GPL seems like an odd choice considering the rest of the Leaflet environment mostly uses MIT / Appache Licenses. Many companies also do not consider using GPL licenses due to legal uncertainties. Any chance you would consider changing the license?
Hi there,
I get the following error when I try to call the saveTile function:
core.js:1449 ERROR Error: Uncaught (in promise): TypeError: WEBPACK_IMPORTED_MODULE_2_leaflet_offline.saveTile is not a function
TypeError: WEBPACK_IMPORTED_MODULE_2_leaflet_offline.saveTile is not a function
it's ionic-angular project.
I imported the modules correctly, since getStorageInfo function works fine without any errors...
help please
Hi just wondering if it's possible when you're offline to replace any missing tiles with scaled versions of the closest zoom level available. I've seen a phone app that does this and it works pretty good.
Is TileManager supposed to be included in the package? It's in the source on the master branch and its methods are referenced in the github pages example but it doesn't actually show up in the NPM package.
Steps to replicate the issue:
Install the library and inspect the src files and bundle.js
.
And/Or:
Inspect the npm tarball itself using a tool such as list-npm-contents.
This is the list of all the files in the latest package version:
package.json
.eslintrc.js
.travis.yml
documentation.yml
index.html
karma.conf.js
LICENSE
README.md
rollup.config.js
dist/bundle.js
docs/api.md
src/ControlSaveTiles.js
src/images/save.png
src/index.js
src/localforage.js
src/TileLayerOffline.js
test/SaveTilesTest.js
test/TileLayerTest.js
Hi,
your plugin looks great :)
I'm planning to integrate it in my leaflet application, but I'd prefer to have the download/delete button in the layercontrol next to the layer selector.
Something like this (not tested):
var control = L.control.savetiles(offlineLayer, {
...
});
var baseMaps = {
"OfflineLayer <button onclick="control._saveTiles()">...</button>": offlineLayer
};
Do you think this could be supported by the plugin or do you know a nicer way to do this?
Thanks :)
Hi. This is the first time I've posted an issue so apologies if its a silly one. I've been attempting to integrate the plugin into my website maps, however I seem to encounter a CORS issue at the GET tiles step. My data is stored in an AWS bucket, which has a CORS * policy, allowing access. The JS loads the tiles correctly for viewing however when saving I get the following error:
Access to XMLHttpRequest at 'FILE PATH' from origin 'https://bathymaps.com.au' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. My website htaccess file allows CORS, so I'm assuming its something to do with the way the JS requests to GET the tiles from the bucket? Any help would be great.
Hello,
There is a CROS origin issue, please try to download some tiles, you will get a message like this:
Failed to load resource: the server responded with a status of 404 (Not Found)
(index):1 Access to XMLHttpRequest at 'http://a.tile.openstreetmap.org/13/4209/2706.png' from origin 'http://allartk.github.io' has been blocked by CORS policy: The response is invalid.
Hi there, I think the bundle.js
that's currently in the npm 1.0.4 package does not have the latest changes in it. The src
is all current, so I think you just need to run npm build
and then npm publish
to get the dist/bundle.js
up to date.
I ended up forking the package to add an option that limits the maximum number of tiles that's stored locally, but I also had to rebuild before that just to get the latest commit.
Thanks for this awesome library! Let me know if you have any questions about my issue.
Hi!
So, the way it is now, the tiles are saved based on the zoomLevels parameter that you pass for the control. I would like to do it more dynamic instead of hardcoding the zoom levels. For example, the user goes to the place and zooms out and clicks save. Then the layer would save the zoom that he is at at the moment + deeper zooms based on a maxZoom parameter (or a max one that we determine if maxZoom is not provided).
Do you want a pull request for that also? Or should I leave that feature only in my fork? I'm not sure if I like the idea of passing the zoomLevels array to determine what you're gonna save because it's not really what the user sees.
I've recently started to make our maps work offline and this plugin has been a huge help.
But I've noticed a small Problem. We use the 'load' event in the Tilelayer that is supposed to fire when all the tiles in view are loaded.
Before switching to your plugin the event was fired exactly once on loading the map before any zooming or panning happens. Now after the switch the event fires about 30 times.
I've seen that this plugin has a 'loadend' event but as far as I can tell this only fires when downloading tiles with the SaveTiles control.
hi,
i tested the demo and found that when closing the browser all downloaded tiles are gone
how can i download tiles and keep them in browser local storage between sessions (permanent until refreshed by the user)
Hi,
When saving about 3000 tiles, it just gives net::ERR_INSUFFICIENT_RESOURCES in chrome error.
I do understand that it would be an issue downloading like 20k+ tiles, but I would expect it to be possible do download about 3k?
This is without any subdomains. Which my tileLayer does not have.
When installed via npm and imported in my main.js file I get the following error upon compiling the project:
Error Failed to compile with 1 errors
This dependency was not found:
* leaflet.offline in ./src/main.js
To install it, you can run: npm install --save leaflet.offline
Steps to duplicate:
vue create NEW_PROJ
npm install leaflet idb
npm install leaflet.offline
node_modules
main.js
: import 'leaflet.offline'
npm run serve
Trying out this plugin for the first time, I have a few questions (maybe for the README?):
How many tiles can the browser download before reaching the limits of IndexedDB storage, and how do we manage (detect, purge older tiles etc) when they have been reached?
More generally how would you recommend to remove older tiles?
How to connect an action from my own interface (i.e. not the offline controls) to the "download all the visible region" function?
thanks!
How i get this work with leaflet providers (https://github.com/leaflet-extras/leaflet-providers)?
Thanks in advance.
Trying to write code to download and save tiles using API calls instead of L.control from the example. Any insights?
` const bounds = map.getBounds();
const zoom = map.getZoom();
const crs = map.options.crs;
const p1 = crs.latLngToPoint(bounds.getNorthEast(), zoom).floor();
const p2 = crs.latLngToPoint(bounds.getSouthWest(), zoom).floor();
const tiles = LeafletOffline.getTileUrls(baseLayer, L.bounds(p1, p2), zoom);
for (i = 0; i < tiles.length; i++) {
LeafletOffline.downloadTile(tiles[i].url).then(blob => LeafletOffline.saveTile(tiles[i], blob));
}`
This is the error I'm getting when it's calling saveTile:
Failed to execute 'put' on 'IDBObjectStore': Evaluating the object store's key path did not yield a value.
Hi,
I believe I'm using leaflet.offline correctly but I'm getting weird results. I copy/pasted from the example into my own project. Now all of the functionality appears to work. I can monitor the download progress, see how many tiles are currently saved etc. However, when trying to load the tiles with no internet connection they never load.
I dug into the chrome application tab under developer tools and think I found the reason. (note, the example suffers from the same problem)
The forage section under IndexedDB appears empty, and all the tile entries have undefined in the Value column.
Any help would be appreciated, thanks :)
I love how this has been implemented but I've found one thing that prevents me from using it. It is unusable for downloading anything more than a couple thousand tiles at a time - it essentially brings the browser to a halt.
I've been learning npm and more advanced javascript stuff and trying to understand in detail how all this works and believe that implementing a webworker to do the tile downloading and saving to idb would resolve this issue.
Any thoughts on that approach?
This is really useful!
One thing - I seem to only able to save tiles on the current zoom levels - I'm getting bad tile urls ( leading to 404 errors, for example this one: http://b.tile.osm.org/16/134708/86612.png ) as soon as multiple zoom levels are specified in options.zoomlevels
@allartk have you been seeing this behaviour? Not sure what the cause is.. any help appreciated!
well done for all this work.
I would like to be able to save the tiles without button directly when opening the map, without clicking (without L.control).
it's possible ?
Thank you.
We use Leaflet to display a base map using Here Map tiles and then display our customer's own map data as overlays. That data is either WMS or ESRI which we display using the ESRI Leaflet plugin. Is it possible to save these tiles for offline display along with the base map tiles?
Hi,
thanks for your efforts with this project!
Just wanted to ask if you ever tested Safari (8.x) with your code? Seems like the indexDB stuff is failing...looks like Safari cannot store blobs like old chrome...
Error uncatched: [Error] DataCloneError: DOM IDBDatabase Exception 25: The data being stored could not be cloned by the internal structured cloning algorithm.
Error message catched exception: DataCloneError: DOM IDBDatabase Exception 25
When using tilelayer.offline and adding it to a map. The leaflet offline script will make a call to load "/undefined" resource.
This can be tested by loading the example site and seeing the network request for http://allartk.github.io/leaflet.offline/undefined.
Should there be anything at this path?
Also, when I save some tiles on a base layer and recreate the Save/Remove controls for a new base layer with L.control.savetiles(baseLayer, {...}), a JS error saying the function is not a callback is thrown. Is there an additional step to working with multiple base layers? Thanks.
Hi there,
I'm having a small problem with "setStorageSize()". You can find the problematic line here:
https://github.com/allartk/leaflet.offline/blob/master/src/ControlSaveTiles.js#L79
You need to check if the callback actually exists. Should be:
if (this.status.storagesize) {
if(callback)
callback(this.status.storagesize);
return;
}
This should probably also be fixed in the catch clause in that same function.
Thanks!
I reviewed the code but can't find the specific.
using cartodb's basemap url:
https://cartodb-basemaps-{s}.global.ssl.fastly.net/light_all/{z}/{x}/{y}.png
The plugin appears to come up with:
https://a.rtodb-basemaps-a.global.ssl.fastly.net/light_all/6/56/28.png
Not sure how it's doing the mangling.
De cors headers are not present anymore requesting tiles from openstreetmap, according to my local firefox. Cannot be replicated in chromium
Cross-Origin-aanvraag geblokkeerd: de Same Origin Policy staat het lezen van de externe bron op http://a.tile.openstreetmap.org/13/4209/2706.png niet toe. (Reden: CORS-header ‘Access-Control-Allow-Origin’ ontbreekt).
Hello,
I'm using version 2.0.0-beta.4, and I'm trying to use the static functions from TileManager.
I am using React with TypeScript and Electron.
The important as written in the docs
import * from 'leaflet.offline/TileManager';
doesn't work for me, it says I have to use 'as', such as:
import * as Offline from 'leaflet.offline/TileManager';
than I get:
Module not found: Error: Can't resolve 'leaflet.offline/TileManager'
I tried to see what auto completion has to offer, and the only way I can get it to work is as follows:
import * as Offline from 'leaflet.offline/src/TileManager';
I saw that inside the bundle.js file all of those functions exists, I just can't get to them. On a similar issue, Inside the module, I saw that the index.js is exporting only part of the functions (getStorageInfo, getTileUrls, getStorageLength, truncate, getStoredTilesAsJson, removeTile), is there a reason for it?
Thanks a lot!
Hi,
First of all this is a great project and it works as advertise, the question is can you save specific tiles on specific zoom then save it then distribute this as part of the application? I'm planning to create hybrid up and wrap it with cordova (Offline Map Navigation)?
One more question :)
I'm thinking about manually setting the bounds of the downloaded area to the scope off my app. E.g. via options.
This way you only have to download once and you have covered the whole service area of my mountain rescue department.
What do you think about that?
I think I could do a pull-request within next week
I just tried updating leaflet
to the new version 1.7.1 in one of my projects that also uses leaflet.offline
and the project just stopped working because L.tileLayer.offline
was undefined. After looking around for a bit I noticed, that while my project was now using leaflet 1.7.1, leaflet.offline was still importing a separate version of leaflet 1.6.0
I'm using yarn
as a package manager (not sure if it also happens with npm
) and I was finally able to resolve it by just throwing away my yarn.lock
and doing a fresh install.
I don't have much experience with maintaining packages, but I think leaflet.offline
being a plugin for leaflet
would be a good case for leaflet
being a peerDependency rather than a normal dependency of the package.
Or is there any reason why it needs to be a direct dependency?
Current iOS Safari seems to have a bug with indexedDB right now. It is already reported on the idb library:
jakearchibald/idb#227
jakearchibald/idb-keyval#120
So I'm not sure if we need a new version here right now. But in case any one else has this problem there is an easy workaround for now. As mentioned in the second issue putting window.indexedDB.open('test');
before using leaflet.offline
fixes the problem.
Hi, I started using this lib for a project and some features would be easier to add in its source code instead of hacking my code so I don't need to alter the code from libraries. For example I need a confirmation that the user wants to delete all the tiles (it shouldn't be too hard). Is that something that interests you? I can send pull requests if you'd like help with it.
_getStorageKey doesn't allow for attributes such as accessToken or id to be passed into layer.
Use case:
var offlineLayer = L.tileLayer.offline('https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token={accessToken}', {
id: 'mapbox.streets',
accessToken: 'xyz'
}).addTo(map);
Gives error:
"Error: No value provided for variable {id}"
_getStorageKey(coords) {
return getTileUrl(this._url, {
...coords,
s: this.options.subdomains['0'],
});
},
Should be something similar to this:
_getStorageKey(coords) {
return getTileUrl(this._url, Object.assign({
...coords,
s: this.options.subdomains['0'],
}, this.options));
To allow different attributes to be passed in
Full diagnosis:
Error: Request failed with status 200
at XMLHttpRequest.xhr.onreadystatechange [as __zone_symbol__ON_PROPERTYreadystatechange] (http://localhost:8100/build/vendor.js:174277:15)
at XMLHttpRequest.H (http://localhost:8100/build/polyfills.js:3:23950)
at t.invokeTask (http://localhost:8100/build/polyfills.js:3:15660)
at Object.onInvokeTask (http://localhost:8100/build/vendor.js:5125:33)
at t.invokeTask (http://localhost:8100/build/polyfills.js:3:15581)
at r.runTask (http://localhost:8100/build/polyfills.js:3:10834)
at e.invokeTask [as invoke] (http://localhost:8100/build/polyfills.js:3:16794)
at p (http://localhost:8100/build/polyfills.js:2:27648)
at XMLHttpRequest.v (http://localhost:8100/build/polyfills.js:2:27893)
I am using your script in Ionic Framework, installed through npm install leaflet.offline
.
I have checked my storage, the script does seem to capture the tiles into db
Another problem is, type script won't recognise .offline
and .saveTiles
.
My compiler gives this error message:
[14:51:46] typescript: src/pages/home-map-view2/home-map-view2.ts, line: 45
Property 'offline' does not exist on type 'typeof tileLayer'.
[14:51:46] typescript: src/pages/home-map-view2/home-map-view2.ts, line: 52
L44: //offline baselayer, will use offline source if available
L45: var baseLayer = leaflet.tileLayer.offline('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
L46: attribution: 'Map data {attribution.OpenStreetMap}',
Property 'savetiles' does not exist on type 'typeof control'.
Please help. Thanks.
Hey this project is really cool! Thanks for working on it!
I was wondering if it is possible to have a concept of multiple maps with this implementation.
What I mean by that is presume you have a hiking application with two different suggested hikes.
You save the map for 'hike A' and then save the map for 'hike B' then you could individually remove the tiles meant for 'hike A' or 'hike B' or else just prompt the storage to load only the tiles for 'hike A'.
I hope this makes sense.
Thanks
I am having an issue similar to #2, but on my own map not the example one. I am using a TMS style tile directory generated with gdal2tiles
and serving it locally. The map loads just fine but If I set saveWhatYouSee
to be true I only get correct URLs for the current zoom level, after that it requests tiles that don't exist and I get uncaught 404 request errors:
Uncaught Error: Request failed with status 404 at XMLHttpRequest.n.onreadystatechange
However, if I manually go to a lower zoom level (e.g. 16 -> 17) I can save the tiles at the new zoom level but not any deeper (17 will save but not 18).
The tile numbers almost looks like the TMS: true
value isn't being propagated to the deeper zoom levels but am not sure if that makes sense or not.
I can share code but am not sure how much it will help as I'm using VueJS and Vue2Leaflet so the set up is quite different from the example. I don't expect you to be able to debug my code but am wondering if you are aware of any issues that could cause this behavior.
Here's my component code in case it's useful. The map is in a div in my index.html file.
<template>
<div>
<Observation/>
<br>
<b-container fluid>
<b-row align-v="center">
<b-col>
<p>
Progress: <span id="progress">0</span> / <span id="total">0</span>
Current storage length:
<span id="storage"></span> files
<button
class="btn btn-success"
id="show_storage"
data-toggle="modal"
data-target="#storageModal"
>
Show storage info
</button>
<button class="btn btn-danger" id="remove_tiles">
<i
class="fa fa-trash"
aria-hidden="true"
title="Remove tiles"
></i>
</button>
</p>
</b-col>
</b-row>
<b-row align-v="center">
<b-col>
<l-map class="centered" ref="myMap" style="height: 400px; width: 800px" :zoom=16 :center="emancipation" @click="openObservationForm">
<v-locatecontrol/>
</l-map>
</b-col>
</b-row>
</b-container>
<br/>
<br/>
</div>
</template>
<script>
import L from 'leaflet';
import 'leaflet.offline'
import Observation from "./Observation";
export default {
name: "ExampleMap",
components: {
Observation
},
mounted() {
this.$nextTick(() => {
var map = this.$refs.myMap.mapObject;
this.baseLayer.addTo(map);
this.control.addTo(map);
})
},
data() {
return {
map: {},
titleProvider: {
name: 'ABR Peat Ponds',
visible: true
},
// pondLocation: [64.910603, -147.940754],
emancipation: [64.912736, -148.262492],
// pondLocation: [48.858, 2.294],
// location: [51.985, 5],
baseLayer: L.tileLayer.offline('https://example_internal_server/emancipation_tiles/{z}/{x}/{y}.png', { tms: true, opacity: 1.0 }),
}
},
computed: {
control() {
return L.control.savetiles(this.baseLayer, {
saveWhatYouSee: true,
maxZoom: 18,
// zoomlevels: [15, 16, 17, 18],
confirm(layer, succescallback) {
// eslint-disable-next-line no-alert
if (window.confirm(`Save ${layer._tilesforSave.length}?`)) {
succescallback();
}
},
confirmRemoval(layer, successCallback) {
// eslint-disable-next-line no-alert
if (window.confirm('Remove all the tiles?')) {
successCallback();
}
},
saveText: '<i class="fa fa-download" aria-hidden="true" title="Save tiles"></i>',
rmText: '<i class="fa fa-trash" aria-hidden="true" title="Remove tiles"></i>',
})
}
},
methods: {
openObservationForm(event) {
this.$bvModal.show('observation');
// eslint-disable-next-line
console.log(event.latlng);
// this.$refs.myMap.mapObject.addMarker(event.latlng);
},
}
}
</script>
<style scoped>
@media (min-width: 768px) and (max-width: 1024px) {
.mobile-map-size {
width: 100% !important;
height: 50% !important;
}
}
</style>
Hello!
Do you remember why is that for necessary?
leaflet.offline/src/Control.SaveTiles.js
Lines 120 to 122 in e4eefac
When we are saving tiles, the map does not update on a change of zoom, move.
The area being saved remains but the rest is grey.
The map updates when the saving is finished.
If a provided tile URL results with anything other than a 200
status code then the _loadTile
method gets interrupted and stops processing the queue of tiles.
Right now the method only handles the case of a 200 response, and that's not a safe assumption to make. There's essentially no error handling and there's any number of ways in which the request could fail.
Our specific case has to do with Mapbox providing us some stale/invalid tile URLs and we do not have the resources right now to correct the problem at its source.
I am working on a fix for this in my own fork but I wanted to make note of it here as well.
<a href="#">
controls should have role="button"
<a>
controls themselves should have the title
attributes, or at the very least remove aria-hidden="true"
in (e.g.):leaflet.offline/docs/src/index.js
Lines 31 to 34 in ecbebae
title
s are currently hidden from screen reader users, due to being placed on <i>
which is aria-hidden="true"
)Helpful resources:
I'm trying to use this library in ionic with Angular but I can't get it to work.
I install as follows
Inside my .ts file I import it
import { Map, latLng, tileLayer, Layer, marker, icon } from 'leaflet';
import { TileLayerOffline } from 'leaflet.offline';
@Component({
selector: 'app-tab1',
templateUrl: 'tab1.page.html',
styleUrls: ['tab1.page.scss']
})
export class Tab1Page {
mapL: L.Map;
baseLayer: TileLayerOffline;
constructor()
{ }
initLeaflet() {
// In setView add latLng and zoom
this.mapL = new Map('mapId');
this.baseLayer = new TileLayerOffline('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: 'LOO ©',
}).addTo(this.mapL);
}
Save timestamp, to allow deletion of older tiles
Hi there, I'd like to know if there's a way to pass saveWhatYouSee option to save control, but instead of saving the current zoom level, plus deeper zoom leves, I'd like to save only the current zoom level, so if my view of the map is on a low zoom level, I won't start saving thounsands of tiles of the deeper levels, if I move my map zoom to a greather zoom level, let's say 9 -- > 10 I got save tiles from the view I got from level 9 zoom before zooming in, and tiles from level 10 zoom, but just my current bounds of level 10, I hope I made miself clear, and I hope you guys can help me out, thanks.
PD: Awesome plugin btw.
UPDATE: Hey I already accomplished what I was trying to do, I add another option to L.control.savetiles, naming it "saveCurrentZoom" then on _saveTiles function did this
zoomlevels.push(this._map.getZoom()); and add and zoomend event to my map, inside this event I call control._saveTiles() and that was it, pretty simple actually, thanks for this plugin and sorry for opening an issue for something that simple. Thank you.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.