make leaflet offline map (use local map tiles) but nothing shows up - javascript

it's work on leaflet online map like this
<script>
import L from 'leaflet';
export default {
mounted() {
var map = L.map('map').setView([25.042474, 121.513729], 13);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 13,
minZoom:2
}).addTo(map);
},
};
</script>
when i try to make map tiles locally then nothing show up on html
( using MobileAtlasCreator make map tiles)
<script>
import L from 'leaflet';
export default {
mounted() {
var map = L.map('map').setView([25.042474, 121.513729], 13);
L.tileLayer('./img/mapTiles/{z}/{x}/{y}.png', {
maxZoom: 13,
minZoom:2
}).addTo(map);
},
};
</script>
this is my reference below
HTML offline map with local tiles via Leaflet
https://www.youtube.com/watch?v=oP4bCLtXIeY
thanks a lot

Since you are planning to work (or use it afterwards) in a local environment, I would take out the import snippet from the code. It's mounted, I get it; but just work locally, after you get those tiles downloaded.
Put them in a folder, and load them using the code you have.
Your code is fine!
Here is my example:
var m = {x: 41.892594, y: 12.484371};
var SELF_Map = L.tileLayer('empire/{z}/{x}/{y}.jpg', {
//attribution: 'none',
continuousWorld: false,
minZoom: 4,
maxZoom: 10,
tap: false
}).addTo(map);
map.setView({lat: m.x, lng: m.y}, 6);
It worked for me every time. I get the images from my favorite map provider, I download them, and store them in a folder, in this case, a folder called "empire".

Related

Leaflet map not rendering full image properly on mobile devices

I'm currently working in a react web-app. We are using leaflet to render some buildings maps. The app is working good in desktop. But we found the maps images are not loaded properly when using mobile navigator.
The map started loading but seems cropped, until you switch to other app and return to the app/page again you can see the full image. In some pages the image seems cropped but when scroll or zoom-in, zoom-out the image loads well.
I have tried to use invalidateSize() function but seems not working.
useEffect(() => {
let mapAttributes = {
attributionControl: false,
minZoom: -4,
zoomDelta: 0.25,
zoomSnap: 0.25,
wheelPxPerZoomLevel: 150,
crs: L.CRS[coordinateSystem],
zoomControl: isShowZoomControl,
};
const map = L.map(mapRef.current, {
...mapAttributes,
});
L.imageOverlay(backgroundImageUrl, mapBounds).addTo(map);
const defaultMarkerPaneZIndex = 600;
map.createPane(mapPanes.markerNotifications);
map.getPane(mapPanes.markerNotifications).style.zIndex = defaultMarkerPaneZIndex + 1;
setMapObj(map);
return () => {
setMapObj(null);
map.off();
map.remove();
};
}, [
backgroundImageUrl,
buildingUuid,
controlBoundariesCallback,
coordinateSystem,
floorUuid,
isShowZoomControl,
mapBounds,
]);
useEffect(() => {
if (!mapObj) return;
mapObj.invalidateSize();
}, [mapObj);

How to use deck.gl to render a polygon layer that is using a different coordinate system than the standard LAT LONG?

Background
I am using deck.gl's PolygonLayer to render data that looks like this:
data.json:
{
"someKey": "someValue",
"spatialReference": {
"wkid": 23032,
},
"features": [
{
"attributes": {
"polygonName": "MY_POLYGON"
},
"geometry": {
"rings": [
[
[421334, 7240529], ...
],
[
[422656, 7250696], ...
]
]
}
}
]
}
Now, the problem is that decg.gl uses a latitude-longitude coordinate-system, which is different from what this polygon is expressed in.
Deck.GL documentation on rendering layers with different coordinate-systems
So, according to the documentation, deckGL renders each layer separately based on its coordinate system. Therefore, it was important to specify both coordinateOrigin and coordinateSystem props.
Understanding the coordinate system in data.json
So, as far as I understood, the spacialReference value in data.json represents an EPSG code. Using this website, I was able to find a value for the coordinateOrigin prop as [63.510617, 9.210989, 0]. As for the coordinateSystem prop, I used COORDINATE_SYSTEM.METER_OFFSETS. Here's the code:
PolygonLayer.tsx:
import React from "react";
import { COORDINATE_SYSTEM } from "#deck.gl/core/typed";
import { DeckLayer } from "#deck.gl/arcgis";
import { PolygonLayer } from "#deck.gl/layers/typed";
import MapView from "#arcgis/core/views/MapView";
import ArcGISMap from "#arcgis/core/Map";
import "#arcgis/core/assets/esri/themes/light/main.css";
export default function PolygonLayer({layerURL}) {
const mapRef = React.useState(null)
React.useEffect(() => {
fetch(layerURL)
.then(res => res.json())
.then(data => {
const blobURL = new Blob([JSON.stringify(data)], {type: "application/json",});
const url = URL.createObjectURL(blobURL); // this is needed for the layer
const layer = new PolygonLayer({
id: data["features"][0]["attributes"]["polygonName"], // correctly defined
data: url,
filled: true,
getLineWidth: 3,
getLineColor: [255, 255, 255, 0],
getFillColor: [234, 243, 221, 0],
coordinateOrigin: [63.510617, 9.210989, 0], // based on the explanation above
coordinateSystem: COORDINATE_SYSTEM.METER_OFFSETS,
getPolygon: (d) => {
console.log(d); // doesn't log anything
return d.features[0].geometry.rings;
},
});
const deckLayer = new DeckLayer({
"deck.layers": [layer],
});
const arcgisMap = new ArcGISMap({
basemap: "topo-vector",
layers: [deckLayer]
});
new MapView({
container: mapRef?.current,
map: arcgisMap,
center: data["features"][0]["geometry"]["rings"][0][0], // correctly defined
zoom: 9
});
})
.catch(err => console.log(err));
}, [layerURL]);
return <div ref={mapRef} style={{height: "90vh", width: "100%"}}></div>
}
The issue with this code
The problem with this code is that it doesn't render the layer (or the base map) and there's nothing logged in the console for the value of d; as mentioned in the code above.
Making sure the code works
Now, just a sanity check, I have used this url which returns polygons data in the standard LAT LONG format, without using coordinateOrigin or coordinateSystem props as in this example and it worked. So the code is ok rendering LAT LONG system, but breaks when using METERS_OFFSET as in the code provided.
Therefore
Have I figured out the coordinateOrigin correctly? And how can I use this (or another type of) layer to render this data correctly? Any help is appreciated and apologies for the long question!

Ionic 5 Leaflet - Map container not found

I'm trying to integrate a leaflet map into my application.
I've written the following code in my .ts file:
ionViewDidEnter() {
this.leafletMap();
}
leafletMap() {
this.map = Leaflet.map('map').setView([49.992863, 8.247253], 5);
Leaflet.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© XY',
}).addTo(this.map);
}
In the template file I've added the div container: <div id="map" style="width: 100%; height: 200px">
When I'm running it, everything works fine. But now I would like to add some markers with data, which is loaded from an api. Therefore, I have to modify the code so, that the map (and the markers) are added after the data has loaded. So, my new code is the following:
ionViewDidEnter() {
this.dataService.getLocation(this.locationID).then(data => {
this.location = data;
this.leafletMap();
});
}
So now I'm receiving the location data, but I'm also getting an error message: Error: Uncaught (in promise): Error: Map container not found.
I've tried different things from other questions here, but nothing works. Do you have an idea, how I can solve this problem?
can you try this:
leafletMap() {
setTimeout( () => {
this.map = Leaflet.map('map').setView([49.992863, 8.247253], 5);
Leaflet.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© XY',
}).addTo(this.map);
}, 2000);
}
I had the same problem with here maps. This was working for me

My button onClick recenter for OpenLayers doesn't work

I'm building an angular application using openlayers that when I click a button it will recenter my map .I'm trying to re-center my map when I onClick to a button but it doesnt work.
ERROR TypeError: Cannot read property 'setCenter' of undefined.
Can someone tell me what I'm doing wrong. Thanks in advance !
import { Component } from '#angular/core';
import {fromLonLat} from 'ol/proj'
import {view} from 'ol/View';
import * as ol from 'openlayers';
export class AppComponent {
distance = 60;
points: Array<{ x: number; y: number; }> = [];
position : Array<{ x: number; y: number; id: string; radius: number,color:string, place:string}> =
[
{x:11.5820,y:48.1351,id:"munich",radius:20, color:"red", place:"m"},
{x:13.388866,y:52.517071,id:"berlin", radius:40,color:"blue", place:"b"},
];
coords = {
berlin: [13.388866, 52.517071]
};
onClick (city: string) {
view.setCenter({
center: fromLonLat(this.coords[city]),
duration: 2000
});
}
mapOnClick(evt) {
console.log(evt);
const map = evt.map;
// this bit checks if user clicked on a feature
const p = map.forEachFeatureAtPixel(evt.pixel,
function(feature, layer) {
console.log("got feature" + feature.getId());
return feature;
});
}
}
<button id='berlin' (click)="onClick('berlin')">Zoom to Berlin</button>
If you are trying to recenter there must already be a view, but if it was constructed inside the map constructor there won't be a view variable and you will need to reference it using map.getView(). Also setCenter() doesn't do animated recentering. Assuming your map variable is map try:
map.getView().animate({
center: fromLonLat(this.coords[city]),
duration: 2000
})
Lets try this once just for suggestion,
import OlView from 'ol/View';
view: OlView;
ngOnInit() {
this.view = new OlView({
center: fromLonLat(this.coords[city]),
zoom: 3
});
}
I hope its solve your problem if you received proper data for this.coords[city] variable. You need to pass data like this, center: fromLonLat([6.661594, 10.32371]).
For more Reference,
Use OpenLayers 4 with Angular 5
You may get some idea from this above url example.
Thanks,
Muthukumar

Mapbox style changes/breaks on zoom when a layer is added

I have a mapbox map, initialized with the outdoors-v9 style (tried other styles, same behavior). When I add a layer to the map - a marker or a geojson source and zoom the map, the style changes or breaks, I'm not sure which.
This is the map before the zoom
and after the zoom
here are the functions that init the map and add markers
mapboxgl.accessToken = "pk.*******";
buildMap: function() {
const _self = this;
_self.map = new mapboxgl.Map({
container: "map",
style: "mapbox://styles/mapbox/outdoors-v9",
center: [-95.712891, 37.09024],
zoom: 3
});
_self.map.on('load', function() {
_self.map.addSource('route', {
'type': 'geojson',
'data': {
"type": "FeatureCollection",
"features": []
}
});
_self.map.addLayer({
'id': 'route',
'source': 'route',
'type': 'line',
'layout': {
'line-join': 'round',
'line-cap': 'round'
},
'paint': {
'line-color': '#47576A',
'line-width': 3
}
});
});
}
...
const coords = [addressData.longitude, addressData.latitude];
const marker = new mapboxgl.Marker().setLngLat(coords).addTo(this.map);
I am using Vue.js to render the map. Mapbox version v0.45.0
Any help or leads are highly appreciated
Vue data() properties are reactive, they have getters and setters, so, when loading map object or adding vector tiles layer (geojson), Vue tries to add getters & setters to the map & map.layers which causes vue & vue-dev-tools to crash and mess up the map.
If you enable any raster layer, it would work successfully because raster tiles are loaded via the mapbox.css whereas vector tiles being geojson, are added to the map object.
Easiest solution would be to define a non-reactive variable in vue and then re-use it everywhere.
// edit: A correct/recommended way to set non-reactive data: GitHub link
Seems the issue was related with the fact that I'm pushing the marker instance to an observable (a vuejs data field). After pushing the marker instance to an array, the issue disappeared. This comment doesn't really answer why this happens, but hope it helps someone else that might face the same issue
I just faced this issue and realized that I didn't follow the documentation exactly as it was described (jumped right on to coding without reading properly). And the documentation says:
Storing Map object
Take note that it's generally bad idea to add to Vuex or component's
data anything but primitive types and plain objects. Vue adds getters
and setters to every property, so if you add Map object to Vuex store
or component data, it may lead to weird bugs. If you want to store map
object, store it as non-reactive property like in example below.
The problem was that I had also registered "map" inside the "data" object of my Vue component. But in the example code it's not declared in data, only in the "create" function.
https://soal.github.io/vue-mapbox/guide/basemap.html#map-loading
After hours spent on this problem, here is my working solution to access map instance from a store (thanks to https://github.com/vuejs/vue/issues/2637#issuecomment-331913620):
const state = reactive({
map: Object.freeze({ wrapper: /* PUT THE MAP INSTANCE HERE */ });
});
Here is an example with Vue Composition Api:
index.js
import { reactive, computed } from "#vue/composition-api";
export const state = reactive({
map: null
});
export const setMap = (map) => {
state.map = Object.freeze({ wrapper: map});
};
export const getMap = computed(() => state.map.wrapper);
export const initMap = (event) => {
setMap(event.map);
// now you can access to map instance from the "getMap" getter!
getMap.value.addSource("satellite-source", {
type: "raster",
url: "mapbox://mapbox.satellite",
});
getMap.value.addLayer({
id: "satellite-layer",
type: "raster",
source: "satellite-source"
});
};
App.vue
<template>
<MglMap :accessToken="..." :mapStyle="..." #load="onMapLoaded" />
</template>
<script>
import { defineComponent } from "#vue/composition-api";
import { MglMap } from "vue-mapbox";
import { initMap } from "./index.js";
export default defineComponent({
components: {
MglMap
},
setup() {
const onMapLoaded = (event) => {
initMap(event);
}
return { onMapLoaded };
}
});
</script>
I've got the same error.
This happens if you either put the map or the marker on an reactive vue.js instance.
Short and quick answer.
Explanation is similar to #mlb's answer. So you freeze the object to prevent the map from disorientated and for any actions done to the map, call back the data with an extra Object key which in case is 'wrapper'.
<template><MglMap :accessToken="..." :mapStyle="..." #load="onMapLoaded" /></template>
<script>
methods: {
onMapLoaded(event) {
this.mapboxEvent = Object.freeze({wrapper: event.map});
},
panMap(event) {
this.mapboxEvent.wrapper.panTo([lng, lat], {duration: 1000, zoom: 14});
}
}
</script>

Categories