I want to use a leaflet map to be a page's background. And this page has a search function, but this search box is not used to search this map. So my question is how to add a search box on a leaflet map?
Do you have any other solution to use a map to be the background?
like this page: http://directory.spatineo.com/
There are many solutions available to adding a search control to a leaflet map. Some are listed on the Leaflet Plugin page under Search and Popups. The search control needs some data to conduct the search, so you should have access to some data on your map. You can host the data on your map or connect to some remote data source(s).
Search Local Level Locations:
If your search criteria is to retrieve data you hosted on the map, then I recommend the Leaflet Search plugin maintained by Stefano Cudini See a working example on this link.
Read more at: https://gis.stackexchange.com/questions/130623/adding-a-search-box-to-a-leaflet-js-example
Search Global Level Locations:
If you want the search criteria to search for random places around the world (that is the database isn't in your app), then use a custom solution provided by a company like ESRI Leaflet project. See working example this codepen page: 'leaflet map with place search'.
<!DOCTYPE html>
<html>
<head>
<title>LeafletJS with Search Box</title>
<!-- CSS and JS files for Search Box -->
<script src="https://cdn-geoweb.s3.amazonaws.com/esri-leaflet/0.0.1-beta.5/esri-leaflet.js"></script>
<script src="https://cdn-geoweb.s3.amazonaws.com/esri-leaflet-geocoder/0.0.1-beta.5/esri-leaflet-geocoder.js"></script>
<link rel="stylesheet" type="text/css" href="https://cdn-geoweb.s3.amazonaws.com/esri-leaflet-geocoder/0.0.1-beta.5/esri-leaflet-geocoder.css">
</head>
<body>
<div id="map"></div>
<script type="text/javascript">
// This setup the leafmap object by linking the map() method to the map id (in <div> html element)
var map = L.map('map', {
center: [51.517327, -0.120005],
zoom: 1.5,
// minZoom: 1.5,
// maxZoom: 1.5
});
// Start adding controls as follow... L.controlName().addTo(map);
// Control 1: This add the OpenStreetMap background tile
L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors'
}).addTo(map);
// Control 2: This add a scale to the map
L.control.scale().addTo(map);
// Control 3: This add a Search bar
var searchControl = new L.esri.Controls.Geosearch().addTo(map);
var results = new L.LayerGroup().addTo(map);
searchControl.on('results', function(data){
results.clearLayers();
for (var i = data.results.length - 1; i >= 0; i--) {
results.addLayer(L.marker(data.results[i].latlng));
}
});
</script>
</body>
</html>
This solution works with last versions of leaflet and geosearch. First load scripts from unpkg.com (the order is important here).
<link rel="stylesheet" href="https://unpkg.com/leaflet#1.7.1/dist/leaflet.css"/>
<link rel="stylesheet" href="https://unpkg.com/leaflet-geosearch#3.1.0/dist/geosearch.css"/>
<script src="https://unpkg.com/leaflet#1.7.1/dist/leaflet.js"></script>
<script src="https://unpkg.com/leaflet-geosearch#3.1.0/dist/bundle.min.js"></script>
<script>
jQuery(document).ready(function($) {
var map = L.map('map', {
center: [36.979120, -121.899390],
zoom: 5
}); //Creates a leaflet map centered at center [latitude, longitude] coordinates.
L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap',
subdomains: ['a', 'b', 'c']
}).addTo(map); //Creates the attribution box at the top bottom right of map.
const provider = new window.GeoSearch.OpenStreetMapProvider();
const search = new GeoSearch.GeoSearchControl({
provider: provider,
style: 'bar',
updateMap: true,
autoClose: true,
}); // Include the search box with usefull params. Autoclose and updateMap in my case. Provider is a compulsory parameter.
L.marker([51.0, -0.09]).addTo(map).bindPopup('A pretty CSS3 popup.<br> Easily customizable.'); //Creates a marker at [latitude, longitude] coordinates.
});
</script>
<div id="map"></div> // Creates the wrapper cotaining the map
Related
I am new to Leaflet and I'm trying to add the Control Search in my map but I encounter this error
Uncaught TypeError: L.Control.geocoder is not a function
I already added these links
<link rel="stylesheet" href="https://unpkg.com/leaflet-control-geocoder/dist/Control.Geocoder.css" />
<script src="https://unpkg.com/leaflet-control-geocoder/dist/Control.Geocoder.js"></script>
I initialized my map
var map = L.map('map').setView([40.71455, -74.00712], 10);
Then added basemap
var dark_matrix = L.tileLayer('https://tiles.stadiamaps.com/tiles/alidade_smooth_dark/{z}/{x}/{y}{r}.png', {
minZoom: 3,
maxZoom: 20,
attribution: '© Stadia Maps, © OpenMapTiles © OpenStreetMap contributors'
});
dark_matrix.addTo(map);
Add the control like this
L.Control.geocoder().addTo(map);
I also tried this one
var searchControl = L.Control.geocoder();
searchControl.addTo(map);
But still does not work.
Here is the jsfiddle of the project https://jsfiddle.net/mgy01hwx/1/
Just change the order of the script files. The plugin needs leaflet code to be imported first in order to work.
first import leaflet.js
then import leaflet-geocoder.js
<script src="https://unpkg.com/leaflet#1.7.1/dist/leaflet.js" integrity="sha512-XQoYMqMTK8LvdxXYG3nZ448hOEQiglfqkJs1NOQV44cWnUrBc8PkAOcXy20w0vlaXaVUearIOBhiXZ5V3ynxwA==" crossorigin=""></script>
<script src="https://unpkg.com/leaflet-control-geocoder/dist/Control.Geocoder.js"></script>
Demo
I am very new to mapbox and leaflet. I am trying to extend the basic mapbox example here to let a user click on a small thumbnail satellite image that will take them to the satellite view. I have been through the examples of both mapbox and leaflet but see no way to do it. Is it possible? Something how google maps does with the satellite view in the lower left hand corner:
https://www.google.com/maps/place/New+York,+NY/#40.6971494,-74.2598655,10z/data=!3m1!4b1!4m5!3m4!1s0x89c24fa5d33f083b:0xc80b8f06e177fe62!8m2!3d40.7127753!4d-74.0059728?hl=en-US
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>A simple map</title>
<meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
<script src='https://api.mapbox.com/mapbox.js/v3.1.1/mapbox.js'></script>
<link href='https://api.mapbox.com/mapbox.js/v3.1.1/mapbox.css' rel='stylesheet' />
<style>
body { margin:0; padding:0; }
#map { position:absolute; top:0; bottom:0; width:100%; }
</style>
</head>
<body>
<div id='map'></div>
<script>
L.mapbox.accessToken = 'pk.eyJ1IjoiYndhZGFtc29uIiwiYSI6ImNqajZhNm1idDFzMjIza3A2Y3ZmdDV6YWYifQ.9NhptR7a9D0hzWXR51y_9w';
var map = L.mapbox.map('map', 'mapbox.streets')
.setView([40, -74.50], 9);
</script>
</body>
</html>
EDIT: Though this example is mapbox js I really don't care if it is mapbox gl or js. Can be either. ok thanks.
You can use a mapbox static api to get a preview of the satellite image:
<img src="https://api.mapbox.com/styles/v1/mapbox/satellite-v9/static/-74.50000,40.00000,9.0,0,0/300x300?access_token=pk.eyJ1IjoiYndhZGFtc29uIiwiYSI6ImNqajZhNm1idDFzMjIza3A2Y3ZmdDV6YWYifQ.9NhptR7a9D0hzWXR51y_9w"/>
[ https://www.mapbox.com/help/static-api-playground/ ]
Update:
You can use the mapbox/geo-viewport library to calculate centerpoint and zoom for preview, and render event to update preview:
map.on('render', function() {
setMapPreview()
})
function setMapPreview() {
var bounds = map.getBounds().toArray()
bounds = [bounds[0][0], bounds[0][1], bounds[1][0], bounds[1][1]]
// The size of the desired map.
var size = [100, 100];
// Calculate a zoom level and centerpoint for this map.
var vp = geoViewport.viewport(bounds, size, 0, 24, 512);
// Construct a static map url
// https://www.mapbox.com/developers/api/static/
document.getElementById('preview').src =
'https://api.mapbox.com/styles/v1/mapbox/satellite-v9/static/' +
vp.center.join(',') + ',' + vp.zoom + ',0,0/' +
size.join('x') + '?' +
'attribution=false&logo=false&access_token=' + mapboxgl.accessToken;
}
[ https://jsfiddle.net/btv9ogpc/ ]
It's not a problem to add an event click to the preview, and rotate styles:
document.getElementById('preview').addEventListener('click', function () {
map.setStyle('mapbox://styles/mapbox/satellite-v9')
})
[ https://jsfiddle.net/xh74rb83 ]
Sounds like you would be interested by some Leaflet plugins for Layer Switching Controls (mapbox.js is built onto Leaflet, so they should be compatible):
Leaflet.Basemaps: (ISC License)
A tile driven basemaps control for Leaflet.
It allows you to create a user interface control for choosing the basemap used on the map, based on a tile from the underlying tile service.
See the example.
With this plugin, you simply specify some constant tile coordinates to be used as "preview":
map.addControl(L.control.basemaps({
basemaps: basemaps, // Array of Tile Layers.
tileX: 0, // tile X coordinate
tileY: 0, // tile Y coordinate
tileZ: 1 // tile zoom level
}));
Leaflet-IconLayers: (MIT License)
Leaflet control that displays base layers as small icons (demo).
For this plugin, even though the documentation uses different images sized 80x80 pixels as preview icon, you can very well re-use tiles with specific coordinates, and the plugin will resize them to fit its icons:
var map = L.map('map').setView([48.86, 2.35], 5);
var OpenStreetMap_Mapnik = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,
attribution: '© OpenStreetMap'
}).addTo(map);
var OpenTopoMap = L.tileLayer('https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png', {
maxZoom: 17,
attribution: 'Map data: © OpenStreetMap, SRTM | Map style: © OpenTopoMap (CC-BY-SA)'
});
var layers = [{
layer: OpenStreetMap_Mapnik,
title: 'OSM Mapnik',
icon: 'https://a.tile.openstreetmap.org/1/0/0.png'
}, {
layer: OpenTopoMap,
title: 'OSM Topo',
icon: 'https://a.tile.opentopomap.org/1/0/0.png' // Re-use a tile
}];
L.control.iconLayers(layers).addTo(map);
html,
body,
#map {
height: 100%;
margin: 0;
}
<link rel="stylesheet" href="https://unpkg.com/leaflet#1.3.1/dist/leaflet.css" integrity="sha512-Rksm5RenBEKSKFjgI3a41vrjkw4EVPlJ3+OiI65vTjIdo9brlAacEuKOiQ5OFh7cOI1bkDwLqdLw3Zg0cRJAAQ==" crossorigin="" />
<script src="https://unpkg.com/leaflet#1.3.1/dist/leaflet-src.js" integrity="sha512-IkGU/uDhB9u9F8k+2OsA6XXoowIhOuQL1NTgNZHY1nkURnqEGlDZq3GsfmdJdKFe1k1zOc6YU2K7qY+hF9AodA==" crossorigin=""></script>
<link rel="stylesheet" href="https://cdn.rawgit.com/ScanEx/Leaflet-IconLayers/ea9af769/dist/iconLayers.css" />
<script src="https://cdn.rawgit.com/ScanEx/Leaflet-IconLayers/ea9af769/dist/iconLayers.js"></script>
<div id="map"></div>
If you wish you can also use images from mapbox static API as shown in stdob--'s answer.
Is there there a way to add centerpoints created via .getCenter() within an onEachFeature event (see below) to an L.Marker, or similar, object that contains all of the centerpoints created on that event, that can be used by Leaflet.Markercluster?
I thought using featureGroup might be the solution, but apparently not.
I can get unclustered centerpoints show up on the map via the addTo(map) method on L.Marker or L.FeatureGroup, but, unfortunately, when I try to use markerCluster on the objects either of those two create, the the map comes up empty. No error messages are appearing on the console in the brower.
I'm still pretty green at JS, so I have a hunch there's something fundamental I'm missing, perhaps about L.Markercluster itself, and my apologies for any noob errors here.
Libraries:
<!-- Leaflet -->
<link rel="stylesheet" href="https://unpkg.com/leaflet#1.2.0/dist/leaflet.css"
integrity="sha512-M2wvCLH6DSRazYeZRIm1JnYyh22purTM+FDB5CsyxtQJYeKq83arPe5wgbNmcFXGqiSH2XR8dT/fJISVA1r/zQ=="
crossorigin=""/>
<script src="https://unpkg.com/leaflet#1.2.0/dist/leaflet.js"
integrity="sha512-lInM/apFSqyy1o6s89K4iQUKg6ppXEgsVxT35HbzUupEVRh2Eu9Wdl4tHj7dZO0s1uvplcYGmt3498TtHq+log=="
crossorigin=""></script>
<!-- ESRI Leaflet -->
<script src="https://unpkg.com/esri-leaflet#2.0.4/dist/esri-leaflet.js"></script>
<!-- Leaflet-markercluster -->
<link rel="stylesheet" href="https://unpkg.com/leaflet.markercluster#1.0.6/dist/MarkerCluster.css"></script>
<link rel="stylesheet" href="https://unpkg.com/leaflet.markercluster#1.0.6/dist/MarkerCluster.Default.css"></script>
<script src="https://unpkg.com/leaflet.markercluster#1.0.6/dist/leaflet.markercluster.js"></script>
<!-- Leaflet.MarkerCluster.LayerSupport -->
<script src="https://unpkg.com/leaflet.markercluster.layersupport#1.0.5/dist/leaflet.markercluster.layersupport.js"></script>
Script:
<script>
var map = L.map('map', {
center: [42.389810, -72.524684],
zoom: 5
});
var esriTopo = L.esri.basemapLayer('Topographic').addTo(map);
var ProjectMap = L.esri.featureLayer ({
url: 'https://services.arcgis.com/2gdL2gxYNFY2TOUb/arcgis/rest/services/NECSC_Test_Data/FeatureServer/1',
//cheap hack to making the polygons invisible
weight: 0,
fillOpacity: 0,
// creating the centerpoints
onEachFeature: function(feature,layer){
if (feature.geometry.type = 'Polygon') {
var bounds = layer.getBounds();
var center = bounds.getCenter();
var centerpoints = L.marker(center);
centerpointlayer.addLayer(centerpoints);
// centerpointlayer defined below as global variable
};
};
}).addTo(map);
var centerpointlayer = L.featureGroup();
//
var clusters = L.markerClusterGroup.layerSupport();
clusters.addTo(map);
clusters.checkIn(centerpointlayer);
map.zoomIn(5);
map.zoomOut(5);
</script>
</body>
</html>
Gah...Turns out I was implementing L.Markercluster wrong (i.e., not like it says in the API docs). The final lines of code before /script at the end should read:
var centerpointlayer = L.layerGroup();
var clusters = L.markerClusterGroup.layerSupport();
clusters.addLayer(centerpointlayer);
map.addLayer(clusters);
This is my first attempt working with javascript and GeoJSON by using leaflet.
So far, I got the desired map and the leaflet.draw plugin working in the way that I can draw a shape and it appears on my screen.
I tried to write this shape to a GeoJSON that I want to use in R.
Therfore I used the ideas presented here to create the GeoJSON string. I think the desired information for me is stored in the variable shape_for_db.
However, using Firebug in Firefox I am not able to find this variable.
Do I get something wrong here?
This is the script I am using:
<html>
<head>
<title>A Leaflet map!</title>
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet/v1.0.0-rc.1/leaflet.css" />
<link rel="stylesheet" href="http://leaflet.github.io/Leaflet.draw/leaflet.draw.css" />
<script src="http://cdn.leafletjs.com/leaflet/v1.0.0-rc.1/leaflet.js"></script>
<script src="jquery-2.1.1.min.js"></script>
<script src="http://leaflet.github.io/Leaflet.draw/leaflet.draw.js"></script>
<style>
#map{ width: 100%; height: 100%; }
</style>
</head>
<body>
<div id="map"></div>
<script>
// base map
var map = L.map('map').setView([51.25,10.57], 8);
// load a tile layer
L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png',
{
attribution: 'Tiles by: OpenStreetMaps',
maxZoom: 17,
minZoom: 5
}).addTo(map);
// Initialise the FeatureGroup to store editable layers
var drawnItems = new L.FeatureGroup();
map.addLayer(drawnItems);
// Initialise the draw control and pass it the FeatureGroup of editable layers
var drawnItems = new L.FeatureGroup();
map.addLayer(drawnItems);
var drawControl = new L.Control.Draw({
edit: {
featureGroup: drawnItems
}
});
map.addControl(drawControl);
map.on('draw:created', function (e) {
var type = e.layerType,
layer = e.layer;
drawnItems.addLayer(layer);
});
// Shape to GeoJSON
map.on('draw:created', function (e) {
var type = e.layerType;
var layer = e.layer;
var shape = layer.toGeoJSON()
var shape_for_db = JSON.stringify(shape);
});
</script>
</body>
</html>
The scope for your shape_for_db is inside your second listener for draw-created. You can place on window.shape_for_db if you are doing this for a one-off experimental/playing around approach and want to use your dev console/Firebug. Or set up var shape_for_db outside the listener.
I am helping my students with a map of some data related to Hurricane Sandy. They have four tile layers (MapBox Street, MapBox Satellite, NOAA Satellite, and NOAA Inundation) and several vector layers.
When it first loads, the map shows the MapBox Streets tiles with some vector data on top, with the ability to switch to the MapBox Satellite layer as the base map. Users can then add the inundation layer on top of the base map (the inundation layer is complex enough that it needs tiles instead of rendered polygons).
The problem occurs when you switch base map layers after the inundation layer has been added to the map. My students use the L.map.addLayer(data, insertAtTheBottom) function to add the new base map layer to the bottom (after removing the old one), but for some reason the layer is only added below the vector layers and not below the inundation tile layer. When the base layers are switching out you can clearly see that the inundation tile layer is still there, it just won't go on top of the new base layer.
Does anyone know why this is the case? The documentation states that this parameter ensures that the new layer will be added below all other layers.
Live example of the page: http://personal.psu.edu/rsm5068/src/map.html
Relevant code segment
<body>
<div id="map"></div>
<div id="controls">
<input type="checkbox" id="toggleSatellite" /><label for="toggleSatellite">Toggle Satellite</label>
</div>
<script type="text/javascript">
var map = null,
layerStreets,
layerSatelliteNow,
layerSatelliteThen,
layer1MSurge,
layer3MSurge,
lgrpBase,
lgrpSurge;
map = L.map("map", {
'center' : L.latLng(40.7127, -74.0059),
'zoom' : 10,
'minZoom' : 9,
'maxZoom' : 16
})
// ---- Tile Layers ----
layerStreets = L.mapbox.tileLayer("user.id");
layerSatelliteNow = L.mapbox.tileLayer("user.id");
layer1MSurge = L.mapbox.tileLayer("user.id");
layer3MSurge = L.mapbox.tileLayer("user.id");
lgrpSurge = L.layerGroup();
lgrpSurge
.addLayer(layer3MSurge)
.addLayer(layer1MSurge);
// ---- Adding Data to Map ----
map
.setMaxBounds(L.latLngBounds(L.latLng(40.4600,-74.33),L.latLng(40.9400,-73.667)))
.addLayer(layerStreets)
.addLayer(lgrpSurge)
.addLayer(fgrpEvacCenters);
// ---- Interactivity ----
$("#toggleSatellite").change(function () {
if ($("input[id='toggleSatellite']:checked").length > 0) {
map
.removeLayer(layerStreets)
.addLayer(layerSatelliteNow, true);
} else {
map
.removeLayer(layerSatelliteNow)
.addLayer(layerStreets, true);
}
})
</script>
</body>
Your Inundation layer should be added to the map as an overlay and not as a basemap (tiled layers can be overlays as well as basemaps).
First you'll want to distinguish between your basemap layers and your overlays.
Your basemap layers (streets, satellite, etc) are those layers that are mutually exclusive and should always be on the bottom.
Your overlay layers are not mutually exclusive and will always sit on top of the basemap layers.
The following example is from the Leaflet LayersControl docs page
var cloudmadeUrl = 'http://{s}.tile.cloudmade.com/API-key/{styleId}/256/{z}/{x}/{y}.png',
cloudmadeAttribution = 'Map data © 2011 OpenStreetMap contributors, Imagery © 2011 CloudMade';
var minimal = L.tileLayer(cloudmadeUrl, {styleId: 22677, attribution: cloudmadeAttribution}),
midnight = L.tileLayer(cloudmadeUrl, {styleId: 999, attribution: cloudmadeAttribution}),
motorways = L.tileLayer(cloudmadeUrl, {styleId: 46561, attribution: cloudmadeAttribution});
var map = L.map('map', {
center: new L.LatLng(39.73, -104.99),
zoom: 10,
layers: [minimal, motorways, cities]
});
var baseMaps = {
"Minimal": minimal,
"Night View": midnight
};
var overlayMaps = {
"Motorways": motorways,
"Cities": cities
};
L.control.layers(baseMaps, overlayMaps).addTo(map);
At this point, the basemaps (Minimal and Night View) will always be underneath the overlays (Motorways and Cities).
Make sure your Inundation layer is added to the map as an overlay and not a basemap.
This I think is a bug. I have tried it too but it is not working correctly. Please use bringToBack() (or bringToFront() if you want it in front) to force layer to correct position:
So:
map.addLayer(layerSatelliteNow);
layerSatelliteNow.bringToBack();