I'm not able to get Leaflet to load the tiles of my map past the first row in this Vue 3 component. I've tried declaring the vertical size of its div several ways, as well as calling map.invalidateSize() when the component is mounted, but without any success.
<script setup lang="ts">
import { onMounted } from "vue";
import * as L from "leaflet";
import "leaflet/dist/leaflet.css";
onMounted(() => {
var map = L.map("map", { attributionControl: false }).setView([0, 0], 0);
L.tileLayer("http://127.0.0.1:7800/{z}/{x}/{y}.jpeg", {
maxZoom: 0,
noWrap: true,
}).addTo(map);
});
</script>
<template>
<h1>Map Detail</h1>
<div id="map"></div>
</template>
<style>
#map {
width: 100%;
height: 512px;
}
</style>
Not sure if either or both will work but you can try
Setting the dimensions of #map like this
#map {
width: 100%;
height: calc(512px / 100%); // Set the height relative to the width of the parent element
}
Setting the maxZoom to a higher value like 18 in tile layer, and set the zoom in map to 1
import { onMounted } from "vue";
import * as L from "leaflet";
import "leaflet/dist/leaflet.css";
onMounted(() => {
var map = L.map("map", {
attributionControl: false,
zoom: 1, // Set the initial zoom level to 1
}).setView([0, 0], 0);
L.tileLayer("http://127.0.0.1:7800/{z}/{x}/{y}.jpeg", {
maxZoom: 18,
noWrap: true,
}).addTo(map);
});
Related
I have a Svelte application with a Map component as below. I'd like to pass in props for centreCoords and Zoom and then fly to them when they change outside the component.
However, I can't work out how to get the map object outside the onMount function.
<script>
import { onMount, onDestroy } from 'svelte';
import mapboxgl from 'mapbox-gl';
const token = 'xxx';
let mapElement;
let map;
export let zoom;
export let centreCoords = [];
// This but doesn't work
map.flyTo({
center: centreCoords,
zoom: zoom,
essential: true // this animation is considered essential with respect to prefers-reduced-motion
});
onMount(() => {
mapboxgl.accessToken = token;
map = new mapboxgl.Map({
container: mapElement,
zoom: zoom,
center: centreCoords,
// Choose from Mapbox's core styles, or make your own style with Mapbox Studio
style: 'mapbox://styles/mapbox/light-v10',
antialias: true // create the gl context with MSAA antialiasing, so custom layers are antialiased
});
});
onDestroy(async () => {
if (map) {
map.remove();
}
});
</script>
<main>
<div bind:this={mapElement} />
</main>
<style>
#import 'mapbox-gl/dist/mapbox-gl.css';
main div {
height: 800px;
}
</style>
Should be possible to just use a reactive statement with an if:
$: if (map) map.flyTo(...)
I'm trying to use the vuelayers API in a vue application. I've imported ol itself too, as I couldn't get the ol-ext module to work. When trying to use the proj functions (specifically addProjection()) I get the following error:
Uncaught TypeError: projection.getCode is not a function
main.js
import { createApp } from 'vue'
import App from './App.vue'
import VueLayers from 'vuelayers'
import 'vuelayers/dist/vuelayers.css'
import 'ol'
createApp(App).mount('#app')
ExampleComp.vue
<template>
<vl-map :load-tiles-while-animating="true" :load-tiles-while-interacting="true" style="height: 400px">
<vl-view :projection="projection" :zoom.sync="zoom" :center.sync="center" :rotation.sync="rotation"></vl-view>
<vl-layer-image id="xkcd">
<vl-source-image-static :url="imgUrl" :size="imgSize" :extent="imgExtent" :projection="projection"
:attributions="imgCopyright"></vl-source-image-static>
</vl-layer-image>
</vl-map>
</template>
<script>
import { addProjection, createProjection } from 'ol/proj'
let size = [1024, 968]
let extent = [0, 0, ...size]
let customProj = createProjection({
code: 'xkcd-image',
units: 'pixels',
extent,
})
addProjection(customProj)
export default {
data () {
return {
zoom: 2,
maxZoom: 8,
center: [size[0] / 2, size[1] / 2],
rotation: 0,
projection: customProj.getCode(),
imgUrl: 'https://imgs.xkcd.com/comics/online_communities.png',
imgCopyright: '© xkcd',
imgSize: size,
imgExtent: extent,
}
},
}
</script>
I don't have enough experience with this to know if the absence of a getCode() function is an issue with the module of my code.
How can we embeded leaflet Map into Preact component. I am creating a Map widget using webpack. In the below I show you the code I implemented.
import registerCustomElement from 'preact-custom-element';
import { h , Component } from 'preact';
import {map as createMap, tileLayer} from '../../node_modules/leaflet/dist/leaflet-src.esm.js';
class mapView extends Component {
componentDidMount() {
let map2 = L.map('mapid').setView([51.505, -0.09], 13);
map.addLayer(L.tileLayer(urlTemplate, {minZoom: 4}));
const mapEl = this.shadowRoot.querySelector('#mapid');
let map = createMap(mapEl).setView([51.505, -0.09], 13);
}
render (){
return (
<div>
<div id="mapid" style="height: 100%"></div>
</div>
)
}
}
registerCustomElement(mapView, "map-view");
Can we use React Leaflet in Preact? Is it possible to use? or is there any library or package to create Map in Preact?
This is easy. I found a Stack Overflow question related to Leaflet usage in LitElement.
My answer is similar as this, but need to do some changes, because I am creating a web component (widget).
import registerCustomElement from 'preact-custom-element';
import { Component } from 'preact';
import {map as createMap, tileLayer , icon, marker} from '../../node_modules/leaflet/dist/leaflet-src.esm.js';
import markerIcon from '../../node_modules/leaflet/dist/images/marker-icon.png';
import '../../node_modules/leaflet/dist/leaflet.css';
import "../Assets/CSS/file.css";
import 'bootstrap/dist/css/bootstrap.css';
class mapView extends Component {
componentDidMount() {
var locations = [
["LOCATION_1", 11.8166, 122.0942],
["LOCATION_2", 11.9804, 121.9189],
["LOCATION_3", 10.7202, 122.5621],
["LOCATION_4", 11.3889, 122.6277],
["LOCATION_5", 10.5929, 122.6325]
];
map = createMap('mapid').setView([51.505, -0.09], 13);
let urlll = 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png?';
map.addLayer(tileLayer(
urlll,
{attribution: 'Map data © OpenStreetMap contributors, Imagery © Mapbox',
maxZoom: 18,
tileSize: 512,
zoomOffset: -1,
})
);
let marketIcon = icon({
iconUrl: markerIcon,
iconRetinaUrl: markerIcon,
iconAnchor: [5, 25],
popupAnchor: [10, -44],
iconSize: [25, 35],
});
for (var i = 0; i < locations.length; i++) {
marker([locations[i][1], locations[i][2]], {
icon: marketIcon,
draggable: true,
}).addTo(map);
}
}
render (){
return (
<div>
<div id="mapid"></div>
</div>
)
}
}
registerCustomElement(mapView, "map-view");
You need to update your webpack.config with the file loader. https://v4.webpack.js.org/loaders/file-loader/
Here is my script file code
<script defer="" src="http://HOST_URL/MapCom.js" type="text/javascript"></script>
<map-view name="map"></map-view>
I am trying to create a map using Leaflet and a Vue component. For some reason the "center: " attribute is not accepting my array coordinates for Latitude and Longitude? When I use it in the template html {{ latlng }} I get an array with the correct coordinates. Any help would be greatly appreciated.
<template>
<div id="mapContainer">{{ latlng }}</div>
</template>
<script>
import "leaflet/dist/leaflet.css";
import L from "leaflet";
import axios from 'axios';
export default {
name: "Map",
data() {
return {
map: null,
latlng: []
};
},
methods: {
get_lat_lng: function(){
axios.get('http://127.0.0.1:5000/api/get_latitude')
.then(res => this.latlng.push(res.data))
axios.get('http://127.0.0.1:5000/api/get_longitude')
.then(res => this.latlng.push(res.data))
}
},
created: function(){
this.get_lat_lng()
},
mounted() {
this.map = L.map("mapContainer", {
center: this.latlng,
zoom: 12,
});
L.tileLayer("http://{s}.tile.osm.org/{z}/{x}/{y}.png", {
attribution:
'© OpenStreetMap contributors'
}).addTo(this.map);
},
beforeDestroy() {
if (this.map) {
this.map.remove();
}
}
};
</script>
<style scoped>
#mapContainer {
width: 50vw;
height: 50vh;
}
</style>
You have hit a race condition here. The mounted() hook is called before the axios.get asynchronous call have been completed.
The sequence of calls is probably this 1.created(), 2.mounted(), 3.axios.then()
So, you will need to slightly change your logic so the map is initialized when both the creation has completed and the mount hook has been invoked.
Something like this,
add a flag for the mounted call been executed
data() {
return {
map: null,
mounted: false,
latlng: null
}
move the map creation code into a method
createMap: function() {
this.map = L.map("mapContainer", { center: this.latlng ...
at creation go fetch the data, and if already mounted, create the map
created(){
axios.all([axios.get('http://127.0.0.1:5000/api/get_longitude');
axios.get('http://127.0.0.1:5000/api/get_latitude')])
.then((longresp,latresp)=> {
this.latlng=[latresp.data,longresp.data];
if (this.mounted) this.createMap()
})
and then at mounted, check if by chance the data is already available, and if not, set the flag for the creation of the map
mounted() {
if (this.latlng) this.createMap()
this.mounted = true;
...
Create the Map when the data is available ( when axios promise is successful ).
More on promises: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
Use a indicator until content not ready.
I think there is no need for separate requests, so i merged the 2 axios requests. Your backend should return the [lat, long] array.
<template>
<div>
<!--
I also added a simple loading indicator,
obviously you can go for something more fancy like a spinner
--!>
<p v-if="loading">Loading...</p>
<div id="mapContainer">{{ latlng }}</div>
</div>
</template>
<script>
import "leaflet/dist/leaflet.css";
import L from "leaflet";
import axios from 'axios';
export default {
name: "Map",
data() {
return {
map: null,
loading: false,
latlng: []
};
},
methods: {
setupLeafletMap: function () {
this.map = L.map("mapContainer", {
center: this.latlng,
zoom: 12,
});
L.tileLayer("http://{s}.tile.osm.org/{z}/{x}/{y}.png", {
attribution:
'© OpenStreetMap contributors'
}).addTo(this.map);
}
},
mounted() {
this.loading = true;
axios.get('http://127.0.0.1:5000/api/get_latlng').then(res => {
this.latlng = res.data;
this.setupLeafletMap(); // Creating the Map here ensures that data is already loaded from server side
this.loading = false;
}).catch(error => console.error(error));
},
beforeDestroy() {
if (this.map) {
this.map.remove();
}
}
};
</script>
<style scoped>
#mapContainer {
width: 50vw;
height: 50vh;
}
</style>
I have my component MAP and I use leaflet (not react-leaflet).
I want to set a marker.
This is the code of my component.
import React from 'react';
import L from 'leaflet';
import '../../../../node_modules/leaflet/dist/leaflet.css';
import './map.scss';
export default class Map extends React.Component {
componentDidMount() {
this.map = L.map('map', {
center: [48.8762, 2.357909999999947],
zoom: 14,
zoomControl: true,
layers: [
L.tileLayer('https://{s}.tile.openstreetmap.de/tiles/osmde/{z}/{x}/{y}.png',{
detectRetina: true,
maxZoom: 20,
maxNativeZoom: 17,
}),
]
});
L.marker([48.8762, 2.357909999999947],
{
draggable: true, // Make the icon dragable
title: 'Hover Text', // Add a title
opacity: 0.5} // Adjust the opacity
)
.addTo(this.map)
.bindPopup("<b>Paris</b><br>Gare de l'Est")
.openPopup();
L.circle([48.8762, 2.357909999999947], {
color: 'red',
fillColor: '#f03',
fillOpacity: 0.5,
radius: 500
}).addTo(this.map);
};
render() {
return <div className="map" id="map" />
}
}
I add the marker us described in the documentation, but the result in the map is not the default icon, it is only a square with a thin border.
I checked the css-File from leaflet. The icon is in the images folder, but it is not in the map available.
This icon (marker) I am looking for
This is what I get
Can anybody help or see what is wrong with my code?
This is a well known css bundle issue with webpack. Define markerIcon as
const markerIcon = L.icon({
iconSize: [25, 41],
iconAnchor: [10, 41],
popupAnchor: [2, -40],
// specify the path here
iconUrl: "https://unpkg.com/leaflet#1.5.1/dist/images/marker-icon.png",
shadowUrl: "https://unpkg.com/leaflet#1.5.1/dist/images/marker-shadow.png"
});
and assign it to the icon property of L.marker like this:
L.marker(
[48.8762, 2.357909999999947],
{
draggable: true, // Make the icon dragable
title: "Hover Text", // Add a title
opacity: 0.5,
icon: markerIcon // here assign the markerIcon var
} // Adjust the opacity
)
.addTo(this.map)
.bindPopup("<b>Paris</b><br>Gare de l'Est")
.openPopup();
Demo
As per your question, I can interpret that you are not able to import the CSS properly.
You can import the css for the leaflet module by writing
import 'leaflet/dist/leaflet.css';
For detailed explanation you can read
Do I have to import leaflet's CSS file?
Example of how to load static CSS files from node_modules using webpack?