Can't load Leaflet inside Vue component - leaflet

I am trying to create a Leaflet map as a Vue component but I am having some difficult getting started. I installed Leaflet through npm
Where am I going wrong? console.log(Leaflet) is returning a Leaflet object but I am having trouble getting the map to expand and render.
Some direction would be appreciated
<template>
<div id="map"></div>
</template>
<script>
// import leaflet here?
import Leaflet from 'leaflet';
export default {
components: {
Leaflet
},
created() {
console.log(this);
console.log(Leaflet);
},
ready() {
this.map = L.map('map').setView([51.959, -8.623], 14);
L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors'
}).addTo(this.map);
}
}
</script>
<style>
#map {
height: 100%;
width: 100%;
margin-top: -24px;
}
/* default legend, layer styling from leaflet template */
.info {
padding: 6px 8px;
font: 14px/16px Arial, Helvetica, sans-serif;
background: white;
background: rgba(255,255,255,0.8);
box-shadow: 0 0 15px rgba(0,0,0,0.2);
border-radius: 5px;
}
.info h4 {
margin: 0 0 5px;
color: #777;
}
.legend {
text-align: left;
line-height: 18px;
color: #555;
}
.legend i {
width: 18px;
height: 18px;
float: left;
margin-right: 8px;
opacity: 0.7;
}
</style>

There are a couple problems in your code:
There is not a ready lifecycle hook in Vue. Use mounted().
You are trying to pass the Leaflet library as a component to Vue, which does nothing since Leaflet is not a Vue component and does not need any sort of registering with Vue.
A map data property was not declared on the Vue component.
There is no problem with importing the library as Leaflet from 'leaflet', but it might be more consistent for yourself or others to declare the Leaflet object as L from 'leaflet'. You can also use: import { Map } from 'leaflet', but then must initialize your Map appropriately: this.map = new Map("mapContainer")
Prevent potential memory leaks and/or cleanup appropriately by using the remove() method on a Leaflet Map Class. A good spot is within the Vue beforeDestroy lifecycle hook.
Also, don't forget to import the Leaflet CSS, for example:
import "leaflet/dist/leaflet.css";
<template>
<div id="mapContainer"></div>
</template>
<script>
import "leaflet/dist/leaflet.css";
import L from "leaflet";
export default {
name: "LeafletMap",
data() {
return {
map: null
};
},
mounted() {
this.map = L.map("mapContainer").setView([51.959, -8.623], 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: 100vw;
height: 100vh;
}
</style>

The correct answer was already provided by jhickok but was insightful for the vue3 composition API as well. The leaflet map can't be instantiated in setup() for the same reasons explained.
<template>
<div id="mapContainer"></div>
</template>
<script>
import { map, tileLayer } from "leaflet";
import { onMounted, onBeforeUnmount } from "vue";
export default {
name: "Map",
setup() {
let container = null;
onMounted(() => {
container = map("mapContainer").setView([51.959, -8.623], 12);
tileLayer("http://{s}.tile.osm.org/{z}/{x}/{y}.png", {
attribution:
'© OpenStreetMap contributors'
}).addTo(container);
});
onBeforeUnmount(() => {
container.remove();
});
}
};
</script>
<style scoped>
#mapContainer {
width: 40vw;
height: 40vh;
}
</style>

first install leafletjs and vue2-leaflet with npm
second copy the code to your component
<template>
<div id="app" v-if="isLoad">
<l-map style="height: 350px" :zoom="zoom" :center="center" #click="addMarker" :max-zoom="maxZoom" :min-zoom="minZoom">
<l-tile-layer :url="url" :attribution="attribution"></l-tilelayer>
<l-marker :lat-lng="markerLatLng" ></l-marker>
</l-map>
</div>
</template>
<script>
import {LMap, LTileLayer, LMarker} from "vue2-leaflet";
import "leaflet/dist/leaflet.css";
import {Icon} from "leaflet";
delete Icon.Default.prototype._getIconUrl;
Icon.Default.mergeOptions({
iconRetinaUrl: require("leaflet/dist/images/marker-icon-2x.png"),
iconUrl: require("leaflet/dist/images/marker-icon.png"),
shadowUrl: require("leaflet/dist/images/marker-shadow.png")
});
export default {
components: {
LMap,
LTileLayer,
LMarker,
},
mounted() {
this.mapIsReady()
},
data() {
return {
isLoad: false,
url: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
attribution: '© OpenStreetMap contributors',
zoom: 15,
maxZoom:18,
minZoom:5,
center: [35.6892, 51.3890],
markerLatLng: [35.6892, 51.3890],
};
},
methods: {
mapIsReady() {
let self = this
setTimeout(() => self.isLoad = true, 1500)
},
addMarker(event) {
console.log(event)
this.markerLatLng = [event.latlng.lat,event.latlng.lng];
},
},
};
</script>
this code has add marker , max zoom and min zoom for your map
this code used open street map because it's free. you can change it to api.mapbox.com if you want but you need api key or accessToken

I just did this, but I needed basemaps too (bing).
leafletWrapper.js
const L = require('leaflet')
require('leaflet-bing-layer')
export default L
LeafletMap.vue
<template>
<div id="map">
</div>
</template>
<script>
import L from 'leafletWrapper'
export default {
name: 'leaflet-map',
props: ['center', 'zoom', 'minZoom', 'maxZoom', 'markers'],
data () {
return {
mapObject: {},
markerLayer: {}
}
},
mounted () {
const bingKey = 'foo'
this.mapObject = L.map(
"map", {
center: this.center,
zoom: this.zoom,
minZoom: this.minZoom,
maxZoom: this.maxZoom
}
)
L.tileLayer.bing({bingMapsKey: bingKey, imagerySet: 'Road'}).addTo(this.mapObject)
},
beforeDestroy () {
this.mapObject.clearAllEventListeners()
}
}
</script>

Related

mapbox moveend trigger on click only

Ive build a map with custom markers and popups just like in this mapbox-tutorial. Besides I have a custom menu with links to fly to those locations and open the associated popups with moveend-event. This works fine so far.
Now I also have a link which zooms out again to fit all markers in viewport. But this also triggers the moveend-event if it gets clicked.
To understand what I mean: See fiddle and click "Washington" then "SF" then "Show all". I dont understand why moveend triggers with "Show all".
Heres a fiddle
HMTL
<div id="map"></div>
<div id="menu">
<span class="link" data-lat="-77.032" data-lng="38.913" data-id="#marker0">Washington</span>
<span class="link" data-lat="-122.414" data-lng="37.776" data-id="#marker1">San Francisco</span>
<span class="showall">Show all</span>
</div>
JS
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/light-v10',
center: [-100, 37.8],
zoom: 0
});
var bounds = new mapboxgl.LngLatBounds();
var geojson = {
type: 'FeatureCollection',
features: [{
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [-77.032, 38.913]
},
properties: {
title: 'Mapbox',
description: 'Washington, D.C.'
}
},
{
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [-122.414, 37.776]
},
properties: {
title: 'Mapbox',
description: 'San Francisco, California'
}
}
]
};
geojson.features.forEach(function(marker, index) {
// create a HTML element for each feature
var el = document.createElement('div');
el.className = 'marker';
el.id = 'marker' + index;
// make a marker for each feature and add to the map
new mapboxgl.Marker(el)
.setLngLat(marker.geometry.coordinates)
.setPopup(new mapboxgl.Popup({
offset: 25
}) // add popups
.setHTML('<h3>' + marker.properties.title + '</h3><p>' + marker.properties.description + '</p>'))
.addTo(map);
bounds.extend(marker.geometry.coordinates);
});
function fitmarkers() {
map.fitBounds(bounds, {
padding: 120,
speed: 1.6
});
}
fitmarkers();
function triggermarker(id) {
map.on('moveend', function() {
$(id).trigger('click');
});
}
function fadeoutpopup() {
if ($('.mapboxgl-popup').length) {
$('.mapboxgl-popup').fadeOut(300, function() {
$(this).remove();
});
};
}
$('.link').click(function() {
$('span').removeClass('active');
$(this).addClass("active");
fadeoutpopup();
var markerid = $(this).data('id');
map.flyTo({
center: [$(this).data('lat'), $(this).data('lng')],
zoom: 11,
speed: 3
});
triggermarker(markerid);
});
$('.showall').click(function(f) {
fitmarkers();
fadeoutpopup();
});
What browser are you using? Why would triggering a moveEnd be a problem? In case you already are moving, this movement has to end, so that the new movement can start (zoom out to show all markers)
When checking your fiddle, it did only work for me after changing the padding to a smaller value than 120:
function fitmarkers(){
map.fitBounds(bounds, {padding: 10, speed: 1.6});
}
Please see the working code below:
(Please be aware, that you have shared your Mapbox access token. I have removed it from below code, but others could use it to generate cost for you)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Display a map</title>
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
<script src="https://api.mapbox.com/mapbox-gl-js/v2.0.0/mapbox-gl.js"></script>
<link href="https://api.mapbox.com/mapbox-gl-js/v2.0.0/mapbox-gl.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<style>
body { margin: 0; padding: 0; }
#map { position: absolute; top: 0; bottom: 0; width: 100%; }
#menu {
position:absolute;
top:0;
padding:20px 40px;
color:#FFF;
background-color:rgba(0,0,0,0.9);
}
#menu span {
margin:0 20px 0 0;
cursor:pointer
}
.link.active {
pointer-events:none;
color:orange
}
.marker {
background-image: url('https://docs.mapbox.com/help/demos/custom-markers-gl-js/mapbox-icon.png');
background-size: cover;
width: 50px;
height: 50px;
border-radius: 50%;
cursor: pointer;
}
.mapboxgl-popup {
max-width: 200px;
}
.mapboxgl-popup-content {
text-align: center;
}
</style>
</head>
<body>
<div id="map"></div>
<div id="menu">
<span class="link" data-lat="-77.032" data-lng="38.913" data-id="#marker0">Washington</span>
<span class="link" data-lat="-122.414" data-lng="37.776" data-id="#marker1">San Francisco</span>
<span class="showall">Show all</span>
</div>
<script>
mapboxgl.accessToken = 'YOUR ACCESS TOKEN';
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/light-v10',
center: [-100, 37.8],
zoom: 0
});
var bounds = new mapboxgl.LngLatBounds();
var geojson = {
type: 'FeatureCollection',
features: [{
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [-77.032, 38.913]
},
properties: {
title: 'Mapbox',
description: 'Washington, D.C.'
}
},
{
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [-122.414, 37.776]
},
properties: {
title: 'Mapbox',
description: 'San Francisco, California'
}
}]
};
geojson.features.forEach(function(marker, index) {
// create a HTML element for each feature
var el = document.createElement('div');
el.className = 'marker';
el.id = 'marker'+index;
// make a marker for each feature and add to the map
new mapboxgl.Marker(el)
.setLngLat(marker.geometry.coordinates)
.setPopup(new mapboxgl.Popup({ offset: 25 }) // add popups
.setHTML('<h3>' + marker.properties.title + '</h3><p>' + marker.properties.description + '</p>'))
.addTo(map);
bounds.extend(marker.geometry.coordinates);
});
function fitmarkers(){
map.fitBounds(bounds, {padding: 10, speed: 1.6});
}
fitmarkers();
function triggermarker(id){
map.on('moveend', function(){
$(id).trigger('click');
});
}
function fadeoutpopup() {
if ($('.mapboxgl-popup').length) {
$('.mapboxgl-popup').fadeOut(300, function() { $(this).remove(); });
};
}
$('.link').click(function() {
$('span').removeClass('active');
$(this).addClass("active");
fadeoutpopup();
var markerid = $(this).data('id');
map.flyTo({
center: [$(this).data('lat'),$(this).data('lng')],
zoom: 11,
speed: 3
});
triggermarker(markerid);
});
$('span.showall').click(function(f) {
fitmarkers();
fadeoutpopup();
});
</script>
</body>
</html>

How to dynamically adjust symbology based on user generated table

I have created a simple leaflet web app that allows for the user to search for the countries that they have traveled.
The user can use a search box or click on the layer on the map. either action will highlight the country green and load the name and flag in an adjacent table.
This works fine. However, problems arise should the user decide to remove a country visited (for example they made a mistake when selecting)
I have included the ability to delete the table items by user click and enabled a function on double click of the layer itself to reset the colour.
What I need is for these two actions to relate to one another. so if the user removes the flag, the related country is reset and vis versa.
I think I need some sort of listener on the click remove or highlight function or perhaps defining the symbology based on the user-generated table of countries visited. Any suggestions?
<!DOCTYPE html>
<html>
{% load static %}
{% load leaflet_tags %}
<head>
{% leaflet_js %}
{% leaflet_css %}
<script type="text/javascript" src="{% static 'dist/leaflet.ajax.js' %}"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<title>hello world</title>
<link rel="stylesheet" href="{% static 'leaflet-groupedlayercontrol/leaflet.groupedlayercontrol.min.css' %}" />
<link rel="stylesheet" href="https://labs.easyblog.it/maps/leaflet-search/src/leaflet-search.css" />
<link rel="stylesheet" href="https://labs.easyblog.it/maps/leaflet-search/examples/style.css" />
<style type="text/css">
#findbox {
background: #eee;
border-radius: 0.125em;
border: 2px solid #1978cf;
box-shadow: 0 0 8px #999;
margin-bottom: 10px;
padding: 2px 0;
width: 600px;
height: 26px;
}
.search-tooltip {
width: 200px;
}
.leaflet-control-search .search-cancel {
position: static;
float: left;
margin-left: -22px;
}
#map {
width: 50%;
height: 650px;
}
ul {
list-style-type: none;
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<p style="text-align: center; font-size: 24px; color: #175b81; font-weight: bold;"></p>
<h3>My World Map</h3>
<div id="findbox"></div>
<div id="map" style="height: 680px; width: 48%; display: ;"></div>
<div class="sidebar" style="display: right;">
<p style="text-align: centre; font-size: 24px; color: #175b81; font-weight: bold;">My Flags:</p>
<table style="width: 90%;">
<tr>
<td id="name" hidden></td>
<td id="code" hidden></td>
</tr>
<ul id="Countries_Visted"></ul>
</table>
</div>
<script>
var img1 = "<img src=";
var im2 = ">";
var quotes = "'";
var flagimage_pt1 = "https://www.countryflags.io/";
var flagimage_pt2 = "/flat/64.png";
// creating mapbox basemap
var mapboxAccessToken =
"pk.eyJ1IjoiY2hyaXNyeWFuLXRlY2giLCJhIjoiY2thY2c3bDZyMDZsNDJ4cHJlZmhwZmFjaCJ9.3GuHRvRz-8fxi4r103z05w";
var mapbox = L.map("map").setView([46.0, 8.0], 2.2);
mapbox.doubleClickZoom.disable();
L.tileLayer(
"https://api.mapbox.com/styles/v1/chrisryan-tech/ckaxvc0bt10041iqfz5fb7dgj/tiles/256/{z}/{x}/{y}#2x?access_token=" +
mapboxAccessToken,
{
tileSize: 512,
zoomOffset: -1,
attribution:
'© Mapbox © OpenStreetMap',
}
).addTo(mapbox);
//loading data with style, click function and listener
var data_layer = new L.GeoJSON.AJAX("{% url 'borders_data' %}", {
style: countriesStyle,
onEachFeature: countriesOnEachFeature,
});
//listener - adding to map on load
data_layer.on("data:loaded", function (e) {
layer.addTo(mapbox);
});
function countriesStyle(feature) {
return {
fillColor: "grey",
weight: 2,
opacity: 0.2,
color: "grey",
dashArray: 3,
fillOpacity: 0.2,
};
}
function countriesOnEachFeature(feature, layer) {
layer.on({
click: function (e) {
var layer = e.target;
layer.setStyle({
weight: 1,
color: "#fff",
dashArray: "",
fillOpacity: 0.9,
fillColor: "green",
});
if (!L.Browser.ie && !L.Browser.opera && !L.Browser.edge) {
layer.bringToFront();
}
},
});
layer.on({
click: load_name_flag,
});
layer.on({
dblclick: clear,
});
}
///// clearing styling
var layer = data_layer;
function clear(e) {
layer.resetStyle(e.target);
}
///// adding to clicked countries to countries visted list
function load_name_flag(e) {
var field1 = document.getElementById("name");
field1.innerHTML = e.target.feature.properties.name;
var field2 = document.getElementById("code");
field2.innerHTML = e.target.feature.properties.iso2;
var selected_name_search = field1.innerHTML;
var selected_flag_search = img1 + quotes + flagimage_pt1 + field2.innerHTML + flagimage_pt2 + quotes + im2;
var selected_features_combine = selected_name_search + selected_flag_search;
$("#Countries_Visted").append("<li>" + selected_features_combine + "</li>").disabled = true;
}
</script>
<!- search box functionality - searches attribute data -->
<!- contained within Geojson from loaded event listener -->
<script src="https://unpkg.com/leaflet#1.3.0/dist/leaflet.js"></script>
<script src="https://labs.easyblog.it/maps/leaflet-search/src/leaflet-search.js"></script>
<script>
//pointing search function to map layer
var searchControl = new L.Control.Search({
container: "findbox",
layer: data_layer,
initial: false,
collapsed: false,
propertyName: "name",
marker: false,
});
// search box finds layer and adds country name and flag, changes layer symbol to green
searchControl.on("search:locationfound", function (e) {
var field1 = document.getElementById("name");
field1.innerHTML = e.layer.feature.properties.name;
var field2 = document.getElementById("code");
field2.innerHTML = e.layer.feature.properties.iso2;
var selected_name_search = field1.innerHTML;
var selected_flag_search = img1 + quotes + flagimage_pt1 + field2.innerHTML + flagimage_pt2 + quotes + im2;
var selected_features_combine = selected_name_search + selected_flag_search;
// styles for selected country
e.layer.setStyle({ fillColor: "green", color: "black", fillOpacity: 0.8 });
/// adding name and flag to countriesvisted list from searchbox select
$("#Countries_Visted").append("<li>" + selected_features_combine + "</li>").disabled = true;
});
mapbox.addControl(searchControl); //inizialize search control
// function to remove added layers
Countries_Visted.onclick = function remove(e) {
var li = e.target;
var layer = data_layer;
var listItems = document.querySelectorAll("li");
var ul = document.getElementById("ul");
li.parentNode.removeChild(li);
layer.resetStyle(e.target);
};
</script>
</body>
</html>

Google Marker GeoChart, create a list of existing markers

Similar to how the Pie Chart has a list of all of the properties in the chart that you can hover:
https://developers.google.com/chart/interactive/docs/gallery/piechart
Is this possible to do in any way with a markers GeoChart? Everything is working fine except that I would really like to have a list of all the cities on my map and to be able to hover on the entries of that list and have the right marker "react" to that. I haven't found anything that seems to make that possible at all.
first, use the following chart option...
tooltip: {
trigger: 'both'
}
this will cause the tooltip to show both when the user hovers and selects the chart.
then on your table / list, when the user hovers a row,
use chart method --> setSelection
this will cause the tooltip to appear.
chart.setSelection([{row: sender.target.parentNode.rowIndex - 1}]);
then clear the selection when the user leaves the table row.
chart.setSelection([]);
see following working snippet...
google.charts.load('current', {
packages: ['geochart', 'table'],
mapsApiKey: 'AIzaSyD-9tSrke72PouQMnMX-a7eZSW0jkFMBWY'
}).then(function () {
var data = google.visualization.arrayToDataTable([
['Country', 'Popularity'],
['Germany', 200],
['United States', 300],
['Brazil', 400],
['Canada', 500],
['France', 600],
['RU', 700]
]);
var containerChart = document.getElementById('chart');
var containerTable = document.getElementById('table');
var chart = new google.visualization.GeoChart(containerChart);
var table = new google.visualization.Table(containerTable);
google.visualization.events.addListener(table, 'ready', function () {
chart.draw(data, {
tooltip: {
trigger: 'both'
}
});
$(containerTable).find('tbody > tr > td').on('mouseenter', markerShow);
$(containerTable).find('tbody > tr > td').on('mouseleave', markerHide);
});
table.draw(data);
function markerShow(sender) {
chart.setSelection([{row: sender.target.parentNode.rowIndex - 1}]);
}
function markerHide(sender) {
chart.setSelection([]);
}
});
html, body {
height: 100%;
margin: 0px 0px 0px 0px;
overflow: hidden;
padding: 0px 0px 0px 0px;
}
#chart {
height: 100%;
}
.flex-row {
display: flex;
flex-direction: row;
height: 100%;
}
.flex-grow {
flex-basis: 0;
flex-grow: 1;
flex-shrink: 1;
}
.flex-static {
flex-grow: 0;
flex-shrink: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div class="flex-row">
<div class="flex-grow">
<div id="chart"></div>
</div>
<div class="flex-static">
<div id="table"></div>
</div>
</div>

How to fix marker in center of leaflet map after user will drag the map?

I am using a custom map on my node add form. My marker is set to my current location using lat and log. Now I want, whenever a user will drag or move map, marker should be in center (fixed). I tried lot of things like:
$cordovaGeolocation.getCurrentPosition(options).then(function(position) {
$scope.latlong = position.coords.latitude + "," + position.coords.longitude;
$rootScope.lati= position.coords.latitude ;
$rootScope.long = position.coords.longitude;
$scope.map.center = {
lat: position.coords.latitude,
lng: position.coords.longitude,
zoom: 20
};
$scope.map.markers.now = {
lat: position.coords.latitude,
lng: position.coords.longitude,
message: "Usted esta aqui!",
draggable: false,
focus: true
};
if ($rootScope.locationresults == undefined) {
Util.getAddressOf(position.coords.latitude, position.coords.longitude).then(function(location) {
$rootScope.locationresults = location[0].formatted_address;
console.log(location);
}, function(error) {
console.error(error);
});
}
$scope.$on("leafletDirectiveMap.move", function(event, args) {
$scope.map.markers.now.setLatLng([0,0]).update();
//$scope.map.markers.now.lat = $scope.map.center.lat;
//$scope.map.markers.now.lng = $scope.map.center.lng;
console.info(JSON.stringify($scope.map.markers.now));
});
$scope.$on("leafletDirectiveMap.drag", function(event, args){
console.log(JSON.stringify($scope.map.center));
//$scope.map.markers.now.setLatLng(0,0);
$scope.map.markers.now.lat = $scope.map.center.lat;
$scope.map.markers.now.lng = $scope.map.center.lng;
});
$scope.$on("leafletDirectiveMarker.dragend", function(event, args) {
console.log("moviendo");
$rootScope.lati= args.model.lat ;
$rootScope.long = args.model.lng;
Util.getAddressOf(args.model.lat, args.model.lng).then(function(location) {
$rootScope.locationresults = location[0].formatted_address;
$scope.latlong = args.model.lat + "," + args.model.lng;
console.log(location);
}, function(error) {
console.error(error);
});
});
}, function(error) {
console.log(error);
});
You could place a fake marker, placing a div with background image on top of the map and placing it with absolute position and pointing always to the center of the map.
Example:
.map-container{
position: relative;
width: 300px;
height: 400px;
}
.map-marker-centered{
background-image: url('https://img.icons8.com/color/48/000000/marker--v1.png') no-repeat;
width: 50px;
height: 60px;
position: absolute;
z-index: 20;
left: calc(50% - 25px);
top: calc(50% - 60px);
transition: all 0.4s ease;
}
<div class="map-container">
<div class="map-marker-centered"></div>
<div class="map"></div>
</div>
Result:
You can place a "fake" marker composed from a background image placed always at the center of the map. You can also detect when map moves and get the coordinates to where the marker points to. Run the snippet:
var mymap = L.map('mapid').setView([51.505, -0.09], 13)
// add the OpenStreetMap tiles
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
{ subdomains: ['a', 'b', 'c'] })
.addTo(mymap)
mymap.on("moveend", function () {
console.log(mymap.getCenter().toString());
});
.map {
height: 280px;
z-index: 1;
}
.map-container {
position: relative;
width: 300px;
height: 300px;
}
.map-marker-centered {
background-image: url("https://img.icons8.com/color/48/000000/marker--v1.png");
width: 50px;
height: 48px;
position: absolute;
z-index: 2;
left: calc(50% - 25px);
top: calc(50% - 50px);
transition: all 0.4s ease;
}
<link rel="stylesheet" href="https://unpkg.com/leaflet#1.7.1/dist/leaflet.css"/>
<script src="https://unpkg.com/leaflet#1.7.1/dist/leaflet.js"></script>
<div class="map-container">
<div class="map-marker-centered"></div>
<div id="mapid" class="map"></div>
</div>

Google place Api, autocomplete and infowindow

following the example on the google developpers website, I would like to have an infowindow open on click within the results of a search (instead of just the tile on hover)
There are a few similar questions but I did't find an answer that does just this.
Can somebody tell me how to fix it please ?
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no">
<meta charset="utf-8">
<style>
html, body, #map-canvas {
height: 100%;
margin: 0;
padding: 0;
}
.controls {
margin-top: 16px;
border: 1px solid transparent;
border-radius: 2px 0 0 2px;
box-sizing: border-box;
-moz-box-sizing: border-box;
height: 32px;
outline: none;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
}
#pac-input {
background-color: #fff;
font-family: Roboto;
font-size: 15px;
font-weight: 300;
margin-left: 12px;
padding: 0 11px 0 13px;
text-overflow: ellipsis;
width: 400px;
}
#pac-input:focus {
border-color: #4d90fe;
}
.pac-container {
font-family: Roboto;
}
#type-selector {
color: #fff;
background-color: #4d90fe;
padding: 5px 11px 0px 11px;
}
#type-selector label {
font-family: Roboto;
font-size: 13px;
font-weight: 300;
}
</style>
<title>Places search box</title>
<script src="https://maps.googleapis.com/maps/api/js?v=3.exp&signed_in=true&libraries=places"></script>
<script>
// This example adds a search box to a map, using the Google Place Autocomplete
// feature. People can enter geographical searches. The search box will return a
// pick list containing a mix of places and predicted search terms.
function initialize() {
var markers = [];
var map = new google.maps.Map(document.getElementById('map-canvas'), {
mapTypeId: google.maps.MapTypeId.ROADMAP
});
var defaultBounds = new google.maps.LatLngBounds(
new google.maps.LatLng(-33.8902, 151.1759),
new google.maps.LatLng(-33.8474, 151.2631));
map.fitBounds(defaultBounds);
// Create the search box and link it to the UI element.
var input = /** #type {HTMLInputElement} */(
document.getElementById('pac-input'));
map.controls[google.maps.ControlPosition.TOP_LEFT].push(input);
var searchBox = new google.maps.places.SearchBox(
/** #type {HTMLInputElement} */(input));
// [START region_getplaces]
// Listen for the event fired when the user selects an item from the
// pick list. Retrieve the matching places for that item.
google.maps.event.addListener(searchBox, 'places_changed', function() {
var places = searchBox.getPlaces();
if (places.length == 0) {
return;
}
for (var i = 0, marker; marker = markers[i]; i++) {
marker.setMap(null);
}
// For each place, get the icon, place name, and location.
markers = [];
var bounds = new google.maps.LatLngBounds();
for (var i = 0, place; place = places[i]; i++) {
var image = {
url: place.icon,
size: new google.maps.Size(71, 71),
origin: new google.maps.Point(0, 0),
anchor: new google.maps.Point(17, 34),
scaledSize: new google.maps.Size(25, 25)
};
// Create a marker for each place.
var marker = new google.maps.Marker({
map: map,
icon: image,
title: place.name,
position: place.geometry.location
});
markers.push(marker);
bounds.extend(place.geometry.location);
}
map.fitBounds(bounds);
});
// [END region_getplaces]
// Bias the SearchBox results towards places that are within the bounds of the
// current map's viewport.
google.maps.event.addListener(map, 'bounds_changed', function() {
var bounds = map.getBounds();
searchBox.setBounds(bounds);
});
}
google.maps.event.addDomListener(window, 'load', initialize);
</script>
<style>
#target {
width: 345px;
}
</style>
</head>
<body>
<input id="pac-input" class="controls" type="text" placeholder="Search Box">
<div id="map-canvas"></div>
</body>
</html>
Thanks.
For some reason, I can't make a jsfiddle with this (maybe it's a domain permission issue) and I can't upload an image since i'm new here
Try putting this into a jsfiddle referencing https://maps.googleapis.com/maps/api/js?v=3.exp&signed_in=true&libraries=places in the External Resources.
After an address/place is selected from the searchbox, the marker and infoWindow render with the infoWindow open and populated.
<div id="map_canvas"></div>
<div id="locationField">
<input id="autocomplete" name="autocomplete" type="text" placeholder="Location Search" onFocus="geolocate()" />
</div>
function initialize() {
var placeSearch;
var mapOptions = {
center: new google.maps.LatLng(41.877461, -87.638085),
zoom: 13,
mapTypeId: google.maps.MapTypeId.ROADMAP,
scrollwheel: false,
disableDefaultUI: true,
streetViewControl: false,
panControl: false
};
var map = new google.maps.Map(document.getElementById('map_canvas'),
mapOptions);
var input = /** #type {HTMLInputElement} */
(
document.getElementById('autocomplete'));
map.controls[google.maps.ControlPosition.TOP_LEFT].push(input);
var autocomplete = new google.maps.places.Autocomplete(input);
autocomplete.bindTo('bounds', map);
var infowindow = new google.maps.InfoWindow();
var marker = new google.maps.Marker({
map: map,
anchorPoint: new google.maps.Point(0, -29)
});
google.maps.event.addListener(marker, 'click', function () {
infowindow.open(map, marker);
});
google.maps.event.addListener(autocomplete, 'place_changed', function () {
infowindow.close();
marker.setVisible(false);
var place = autocomplete.getPlace();
if (!place.geometry) {
window.alert("Autocomplete's returned place contains no geometry");
return;
}
// If the place has a geometry, then present it on a map.
if (place.geometry.viewport) {
map.fitBounds(place.geometry.viewport);
} else {
map.setCenter(place.geometry.location);
map.setZoom(12); // Why 17? Because it looks good.
}
marker.setIcon( /** #type {google.maps.Icon} */ ({
url: place.icon,
size: new google.maps.Size(71, 71),
origin: new google.maps.Point(0, 0),
anchor: new google.maps.Point(25, 34),
scaledSize: new google.maps.Size(50, 50)
}));
marker.setPosition(place.geometry.location);
marker.setVisible(true);
infowindow.setContent('<div><strong>' + place.name + '</strong><br>' +
'Place ID: ' + place.place_id + '<br>' + place.formatted_address);
infowindow.open(map, marker);
});
// Bias the autocomplete object to the user's geographical location,
// as supplied by the browser's 'navigator.geolocation' object.
function geolocate() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function (position) {
var geolocation = new google.maps.LatLng(
position.coords.latitude, position.coords.longitude);
autocomplete.setBounds(new google.maps.LatLngBounds(geolocation, geolocation));
});
}
}
}
initialize();
html, body, #map_canvas {
height: 100%;
width: 100%;
margin: 0;
padding: 0;
}
#autocomplete {
height: 60px;
width: 100%;
border: 2px solid #fff;
color: #1c1c1c;
font-size: 15px;
text-align: center;
background-color: #fff;
font-family: Roboto;
font-weight: 300;
text-overflow: ellipsis;
}
#autocomplete:focus {
border-color: #4d90fe;
}