How to dynamically adjust symbology based on user generated table - leaflet

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>

Related

HexagonLayer - Get aggregated data or it's value bounds

I am building mapbox & deck.gl application and I want to filter some bars at runtime based on value ranges
I want to only show bars, which's aggregated value falls in some range [min,max]
So, my simplified question is
How can I get deck.gl hexagonLayer aggregated bar data after hexagons are drawn on map?
So,then I can correctly calculate upperPercentile and lowerPercentile, which are responsible for graphical filtering...
var { MapboxLayer, HexagonLayer } = deck;
//Create the Mapbox map
var map = new mapboxgl.Map({
container: document.body,
style: 'mapbox://styles/mapbox/dark-v9?optimize=true',
center: [-1.4157, 52.2324],
zoom: 6,
pitch: 40.5
});
// Get Data for visual
var DATA_URL = 'https://raw.githubusercontent.com/uber-common/deck.gl-data/master/examples/3d-heatmap/heatmap-data.csv';
//Create the deck.gl hexagon layer and style for the data
var OPTIONS = ['radius', 'coverage', 'upperPercentile'];
var COLOR_RANGE = [
[1, 152, 189],
[73, 227, 206],
[216, 254, 181],
[254, 237, 177],
[254, 173, 84],
[209, 55, 78]
];
var LIGHT_SETTINGS = {
lightsPosition: [-0.144528, 49.739968, 8000, -3.807751, 54.104682, 8000],
ambientRatio: 0.4,
diffuseRatio: 0.6,
specularRatio: 0.2,
lightsStrength: [0.8, 0.0, 0.8, 0.0],
numberOfLights: 2
};
var hexagonLayer;
map.on('style.load', () => {
hexagonLayer = new MapboxLayer({
type: HexagonLayer,
id: 'heatmap',
data: d3.csv(DATA_URL),
radius: 1000,
coverage: 1,
upperPercentile: 100,
colorRange: COLOR_RANGE,
elevationRange: [0, 1000],
elevationScale: 250,
extruded: true,
getPosition: d => [Number(d.lng), Number(d.lat)],
lightSettings: LIGHT_SETTINGS,
opacity: 1
});
// Add the deck.gl hex layer below labels in the Mapbox map
map.addLayer(hexagonLayer, 'waterway-label');
// I WANT TO GET MINIMUM AND MAXIMUM VALUES FOR BARS HERE
<html>
<head>
<title>Road AccidentsIncidents</title>
<meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
<script src="https://unpkg.com/deck.gl#^6.2.0/deckgl.min.js"></script>
<script src="https://api.tiles.mapbox.com/mapbox-gl-js/v0.51.0/mapbox-gl.js"></script>
<link rel="stylesheet" type="text/css" href="https://api.tiles.mapbox.com/mapbox-gl-js/v0.51.0/mapbox-gl.css">
<script src="https://d3js.org/d3.v5.min.js"></script>
<style type="text/css">
#map { position:absolute; top:0; bottom:0; width:100%; }
body {
font-family: Helvetica, Arial, sans-serif;
width: 100vw;
height: 100vh;
margin: 0;
padding: 0
}
#control-panel {
position: absolute;
background: #fff;
top: 0;
left: 0;
margin: 12px;
padding: 20px;
font-size: 12px;
line-height: 1.5;
z-index: 1;
}
label {
display: inline-block;
width: 140px;
}
</style>
</head>
<body>
<div id="control-panel">
<div>
<label>Radius</label>
<input id="radius" type="range" min="500" max="10000" step="500" value="1000"></input>
<span id="radius-value"></span>
</div>
<div>
<label>Coverage</label>
<input id="coverage" type="range" min="0" max="1" step="0.1" value="1"></input>
<span id="coverage-value"></span>
</div>
<div>
<label>Upper Percentile</label>
<input id="upperPercentile" type="range" min="90" max="100" step="1" value="100"></input>
<span id="upperPercentile-value"></span>
</div>
</div>
</body>
<script type="text/javascript">
mapboxgl.accessToken = 'pk.eyJ1IjoicnNiYXVtYW5uIiwiYSI6ImNqbjdsemZybzFtc3MzcnFvNXFucXQweTEifQ.2N_6Nd1rZG-I2pyA9I1xfA';
var { MapboxLayer, HexagonLayer } = deck;
//Create the Mapbox map
var map = new mapboxgl.Map({
container: document.body,
style: 'mapbox://styles/mapbox/dark-v9?optimize=true',
center: [-1.4157, 52.2324],
zoom: 6,
pitch: 40.5
});
// Get Data for visual
var DATA_URL = 'https://raw.githubusercontent.com/uber-common/deck.gl-data/master/examples/3d-heatmap/heatmap-data.csv';
//Create the deck.gl hexagon layer and style for the data
var OPTIONS = ['radius', 'coverage', 'upperPercentile'];
var COLOR_RANGE = [
[1, 152, 189],
[73, 227, 206],
[216, 254, 181],
[254, 237, 177],
[254, 173, 84],
[209, 55, 78]
];
var LIGHT_SETTINGS = {
lightsPosition: [-0.144528, 49.739968, 8000, -3.807751, 54.104682, 8000],
ambientRatio: 0.4,
diffuseRatio: 0.6,
specularRatio: 0.2,
lightsStrength: [0.8, 0.0, 0.8, 0.0],
numberOfLights: 2
};
var hexagonLayer;
//Add the deck.gl Custom Layer to the map once the Mapbox map loads
map.on('style.load', () => {
hexagonLayer = new MapboxLayer({
type: HexagonLayer,
id: 'heatmap',
data: d3.csv(DATA_URL),
radius: 1000,
coverage: 1,
upperPercentile: 100,
colorRange: COLOR_RANGE,
elevationRange: [0, 1000],
elevationScale: 250,
extruded: true,
getPosition: d => [Number(d.lng), Number(d.lat)],
lightSettings: LIGHT_SETTINGS,
opacity: 1
});
// Add the deck.gl hex layer below labels in the Mapbox map
map.addLayer(hexagonLayer, 'waterway-label');
// I WANT TO GET MINIMUM AND MAXIMUM VALUES FOR BARS HERE
});
// Add sliders to change the layer's settings based on user input
OPTIONS.forEach(key => {
document.getElementById(key).onchange = (evt) => {
var value = Number(evt.target.value);
document.getElementById(key + '-value').innerHTML = value;
if (hexagonLayer) {
hexagonLayer.setProps({
[key]: value });
}
};
});
</script>
</html>
getColorValue function will be called for each cluster with points belonging to that cluster. You can modify this function like:
// other props
// will be called num_cluster times
getColorValue: (points) => {
// points that belong to a particular cluster
// do stuff with points
return points.length;
},
// other props
Finally, you can use the onSetColorDomain callback (think onClusteringComplete callback) to fill in the values into your html. Added advantage here is that this function calls the callback with [min, max], so you don't have to compute them if it's only for showing a bar.
I've altered your example to show number of clusters and min and max.
<html>
<head>
<title>Road AccidentsIncidents</title>
<meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
<script src="https://unpkg.com/deck.gl#^6.2.0/deckgl.min.js"></script>
<script src="https://api.tiles.mapbox.com/mapbox-gl-js/v0.51.0/mapbox-gl.js"></script>
<link rel="stylesheet" type="text/css" href="https://api.tiles.mapbox.com/mapbox-gl-js/v0.51.0/mapbox-gl.css">
<script src="https://d3js.org/d3.v5.min.js"></script>
<style type="text/css">
#map { position:absolute; top:0; bottom:0; width:100%; }
body {
font-family: Helvetica, Arial, sans-serif;
width: 100vw;
height: 100vh;
margin: 0;
padding: 0
}
#control-panel {
position: absolute;
background: #fff;
top: 0;
left: 0;
margin: 12px;
padding: 20px;
font-size: 12px;
line-height: 1.5;
z-index: 1;
}
label {
display: inline-block;
width: 140px;
}
</style>
</head>
<body>
<div id="control-panel">
<div>
<label>Radius</label>
<input id="radius" type="range" min="500" max="10000" step="500" value="1000"></input>
<span id="radius-value"></span>
</div>
<div>
<label>Coverage</label>
<input id="coverage" type="range" min="0" max="1" step="0.1" value="1"></input>
<span id="coverage-value"></span>
</div>
<div>
<label>Upper Percentile</label>
<input id="upperPercentile" type="range" min="90" max="100" step="1" value="100"></input>
<span id="upperPercentile-value"></span>
</div>
<div>
<div style="padding: 7px 0">
<span>Total Clusters: </span>
<span id="total-clusters-value">Loading</span>
</div>
<div>
<span>[Min, Max]: </span>
<span id="min-max-value">Loading</span>
</div>
</div>
</div>
</body>
<script type="text/javascript">
mapboxgl.accessToken = 'pk.eyJ1IjoicnNiYXVtYW5uIiwiYSI6ImNqbjdsemZybzFtc3MzcnFvNXFucXQweTEifQ.2N_6Nd1rZG-I2pyA9I1xfA';
var { MapboxLayer, HexagonLayer } = deck;
//Create the Mapbox map
var map = new mapboxgl.Map({
container: document.body,
style: 'mapbox://styles/mapbox/dark-v9?optimize=true',
center: [-1.4157, 52.2324],
zoom: 6,
pitch: 40.5
});
// Get Data for visual
var DATA_URL = 'https://raw.githubusercontent.com/uber-common/deck.gl-data/master/examples/3d-heatmap/heatmap-data.csv';
//Create the deck.gl hexagon layer and style for the data
var OPTIONS = ['radius', 'coverage', 'upperPercentile'];
var COLOR_RANGE = [
[1, 152, 189],
[73, 227, 206],
[216, 254, 181],
[254, 237, 177],
[254, 173, 84],
[209, 55, 78]
];
var LIGHT_SETTINGS = {
lightsPosition: [-0.144528, 49.739968, 8000, -3.807751, 54.104682, 8000],
ambientRatio: 0.4,
diffuseRatio: 0.6,
specularRatio: 0.2,
lightsStrength: [0.8, 0.0, 0.8, 0.0],
numberOfLights: 2
};
var hexagonLayer;
let clusters = [];
const minMax = document.getElementById('min-max' + '-value');
const totalClusters = document.getElementById('total-clusters' + '-value');
//Add the deck.gl Custom Layer to the map once the Mapbox map loads
map.on('style.load', () => {
hexagonLayer = new MapboxLayer({
type: HexagonLayer,
id: 'heatmap',
data: d3.csv(DATA_URL),
radius: 1000,
coverage: 1,
upperPercentile: 100,
colorRange: COLOR_RANGE,
elevationRange: [0, 1000],
elevationScale: 250,
extruded: true,
getPosition: d => [Number(d.lng), Number(d.lat)],
getColorValue: points => { clusters.push(points.length); return points.length; },
onSetColorDomain: ([min, max]) => { minMax.innerHTML = `[${min}, ${max}]`; totalClusters.innerHTML = clusters.length; },
lightSettings: LIGHT_SETTINGS,
opacity: 1
});
// Add the deck.gl hex layer below labels in the Mapbox map
map.addLayer(hexagonLayer, 'waterway-label');
// I WANT TO GET MINIMUM AND MAXIMUM VALUES FOR BARS HERE
});
// Add sliders to change the layer's settings based on user input
OPTIONS.forEach(key => {
document.getElementById(key).onchange = (evt) => {
clusters = [];
var value = Number(evt.target.value);
document.getElementById(key + '-value').innerHTML = value;
if (hexagonLayer) {
hexagonLayer.setProps({
[key]: value });
}
};
});
</script>
</html>

Leaflet - map layers not loading

I am trying to create a choropleth that shades states depending on the rate of cases in 2010. I am getting the error: Uncaught TypeError: Cannot read property 'features' of null at Object.t.choropleth.n.exports [as choropleth]. Is this a problem with reading and extracting the data from the geojson?
var myMap = L.map('map', {
center: [39.8283, -98.5795],
zoom: 11
});
APIKEY = "pk.eyJ1Ijoic2NvdHRtY2FsaXN0ZXIxMyIsImEiOiJjamlhdWd2bzMxYjU1M3Ztcm54N2kxaDQ2In0.mGtR6lttrtiEpIqHVEIAtQ"
L.tileLayer("https://api.mapbox.com/styles/v1/mapbox/outdoors-v10/tiles/256/{z}/{x}/{y}?access_token=pk.eyJ1Ijoic2NvdHRtY2FsaXN0ZXIxMyIsImEiOiJjamlhdWd2bzMxYjU1M3Ztcm54N2kxaDQ2In0.mGtR6lttrtiEpIqHVEIAtQ").addTo(myMap)
var GEOLINK = "state_data_modified.geojson";
var geojson_0;
d3.json(GEOLINK, function(data){
geojson_0=L.choropleth(data, {
valueProperty: "Rate_2010",
scale: ['white', 'red'],
steps: 5,
mode: "q",
style:{
color:'#fff',
weight: 1,
fillOpacity: .8
},
onEachFeature: function(feature, layer){
layer.bindPopup(feature.properties.State + "<br>Number of cases:<br>" + feature.properties.Cases_2010)
}
}).addTo(myMap);
var legend = L.control({ position: "bottomright" });
legend.onAdd = function() {
var div = L.DomUtil.create("div", "info legend");
var limits = geojson.options.limits;
var colors = geojson.options.colors;
var labels = [];
// Add min & max
var legendInfo = "<h1>Cases and Rates</h1>" +
"<div class=\"labels\">" +
"<div class=\"min\">" + limits[0] + "</div>" +
"<div class=\"max\">" + limits[limits.length - 1] + "</div>" +
"</div>";
div.innerHTML = legendInfo;
limits.forEach(function(limit, index) {
labels.push("<li style=\"background-color: " + colors[index] + "\"></li>");
});
div.innerHTML += "<ul>" + labels.join("") + "</ul>";
return div;
};
legend.addTo(myMap);
});
body {
padding: 0;
margin: 0;
}
#map,
body,
html {
height: 100%;
}
.leaflet-popup-content {
text-align: center;
}
/* CSS from the Leaflet-Choropleth documentation */
.legend {
padding: 6px 8px;
font: 12px Arial, Helvetica, sans-serif;
font-weight: bold;
color: #555;
background: #fff;
background: rgba(255, 255, 255, 0.8);
border-radius: 5px;
box-shadow: 0 0 15px rgba(0, 0, 0, 0.2);
}
.legend ul {
padding: 0;
margin: 0;
clear: both;
list-style-type: none;
}
.legend li {
display: inline-block;
width: 30px;
height: 22px;
}
.legend .min {
float: left;
padding-bottom: 5px;
}
.legend .max {
float: right;
}
h1 {
text-align: center;
}
<!DOCTYPE html>
<html lang="en-us">
<head>
<meta charset="UTF-8">
<title>Test Choropleth</title>
<!-- Leaflet CSS & JS-->
<link rel="stylesheet" href="https://unpkg.com/leaflet#1.0.2/dist/leaflet.css" />
<script src="https://unpkg.com/leaflet#1.0.2/dist/leaflet.js"></script>
<!-- d3 JavaScript -->
<script src="https://d3js.org/d3.v4.min.js"></script>
<!-- Leaflet-Choropleth JavaScript -->
<script type="text/javascript" src="choropleth.js"></script>
<!-- Our CSS -->
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<!-- The div where we will inject our map -->
<div id="map"></div>
<script type="text/javascript" src="test_choro.js"></script>
</body>
</html>
picture of tilelayer and console
For me the error seem pretty clear.
You are loading your geojson from local storage: file:///Users...
This is not possible. You have to make it accessible from an url if you have a server (apache, nginx) or use github to get a raw file url.
You can also use this function:
$.getJSON("your_path/to/state_data.json", function(json) {
console.log(json);
});

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;
}

CartoDB multiple layer toggle

I'm trying to make one map where you can toggle between three different layers, and keep the same legend visible for all. I'm currently following this documentation:
http://docs.cartodb.com/tutorials/toggle_map_view.html
My map in CartoDB has three separate layers (w/ three datasets for the years 2013, 2014 and 2015).
I'm trying to make a toggle map like the one in the documentation. Here's what I go so far:
<html>
<head>
<link rel="stylesheet" href="http://libs.cartocdn.com/cartodb.js/v3/3.11/themes/css/cartodb.css" />
<script src="http://libs.cartocdn.com/cartodb.js/v3/3.11/cartodb.js"></script>
<style>
html, body {width:100%; height:100%; padding: 0; margin: 0;}
#map { width: 100%; height:100%; background: black;}
#menu { position: absolute; top: 5px; right: 10px; width: 400px; height:60px; background: transparent; z-index:10;}
#menu a {
margin: 15px 10px 0 0;
float: right;
vertical-align: baseline;
width: 70px;
padding: 10px;
text-align: center;
font: bold 11px "Helvetica",Arial;
line-height: normal;
color: #555;
border-radius: 4px;
border: 1px solid #777777;
background: #ffffff;
text-decoration: none;
cursor: pointer;
}
#menu a.selected,
#menu a:hover {
color: #F84F40;
}
</style>
<script>
var map;
function init(){
// initiate leaflet map
map = new L.Map('map', {
center: [20,-20],
zoom: 3
})
L.tileLayer('https://dnv9my2eseobd.cloudfront.net/v3/cartodb.map-4xtxp73f/{z}/{x}/{y}.png', {
attribution: 'Mapbox Terms & Feedback'
}).addTo(map);
var layerUrl = 'http://heathermartino.cartodb.com/api/v2/viz/415f8ed2-d493-11e4-b129-0e018d66dc29/viz.json';
var sublayers = [];
cartodb.createLayer(map, layerUrl)
.addTo(map)
.on('done', function(layer) {
// change the query for the first layer
var subLayerOptions = {
sql: "SELECT * FROM gdp_2014",
cartocss: "#gdp_2014{marker-fill: #F84F40; marker-width: 8; marker-line-color: white; marker-line-width: 2; marker-clip: false; marker-gdp_2015ow-overlap: true;}"
}
var sublayer = layer.getSubLayer(0);
sublayer.set(subLayerOptions);
sublayers.push(sublayer);
}).on('error', function() {
//log the error
});
//we define the queries that will be performed when we click on the buttons, by modifying the SQL of our layer
var LayerActions = {
GDP_2015: function(){
sublayers[0].setSQL("SELECT * FROM gdp_2015");
return true;
},
GDP_2014: function(){
sublayers[0].setSQL("SELECT * FROM gdp_2014");
return true;
},
GDP_2013: function() {
sublayers[0].set({
sql: "SELECT * FROM gdp_2013 WHERE cartodb_georef_status = true",
//as it is said, you can also add some CartoCSS code to make your points look like you want for the different queries
// cartocss: "#ne_10m_populated_places_simple{ marker-fill: black; }"
});
return true;
}
}
$('.button').click(function() {
$('.button').removeClass('selected');
$(this).addClass('selected');
//this gets the id of the different buttons and cgdp_2015s to LayerActions which responds according to the selected id
LayerActions[$(this).attr('id')]();
});
L.tileLayer('https://dnv9my2eseobd.cloudfront.net/v3/cartodb.map-4xtxp73f/{z}/{x}/{y}.png', {
attribution: 'Mapbox Terms & Feedback'
}).addTo(map);
}
</script>
</head>
<body onload="init()">
<div id='map'></div>
<div id='menu'>
2013
2014
2015
</div>
</body>
</html>
Right now when you click on the different buttons for 2013, 2014 and 2015, nothing happens. For reference, my map in carto is http://cdb.io/1Bzm2tD. Any ideas? Thanks in advance!
You have the layers. No need to run SQL again. This should work.
<html>
<head>
<link rel="stylesheet" href="http://libs.cartocdn.com/cartodb.js/v3/3.11/themes/css/cartodb.css" />
<script src="http://libs.cartocdn.com/cartodb.js/v3/3.11/cartodb.js"></script>
<style>
html,
body {
width: 100%;
height: 100%;
padding: 0;
margin: 0;
}
#map {
width: 100%;
height: 100%;
background: black;
}
#menu {
position: absolute;
top: 5px;
right: 10px;
width: 400px;
height: 60px;
background: transparent;
z-index: 10;
}
#menu a {
margin: 15px 10px 0 0;
float: right;
vertical-align: baseline;
width: 70px;
padding: 10px;
text-align: center;
font: bold 11px "Helvetica", Arial;
line-height: normal;
color: #555;
border-radius: 4px;
border: 1px solid #777777;
background: #ffffff;
text-decoration: none;
cursor: pointer;
}
#menu a.selected,
#menu a:hover {
color: #F84F40;
}
.cartodb-layer-selector-box,
.cartodb-searchbox,
.cartodb-share {
display: none !important;
}
</style>
<script>
var layer;
function init() {
var url = 'http://heathermartino.cartodb.com/api/v2/viz/415f8ed2-d493-11e4-b129-0e018d66dc29/viz.json';
var visualizacion = cartodb.createVis("map", url)
.done(function(vis, layers) {
layer = layers[1];
});
}
function showLayer(layerToShow) {
//turn off all layers
layer.getSubLayers().forEach(function(i) {
i.hide()
});
switch (layerToShow.id) {
case "gdp_2013":
layer.getSubLayer(0).show();
break;
case "gdp_2014":
layer.getSubLayer(1).show();
break;
case "gdp_2015":
layer.getSubLayer(2).show();
break;
}
return true;
}
</script>
</head>
<body onload="init()">
<div id='map'></div>
<div id='menu'>
2013
2014
2015
</div>
</body>
</html>
I have created something similar - see if this helps. The trick for me was getting the sublayers separated with a for loop, then creating the buttons to act on each.
function loadPosition(position) {
lati = position.coords.latitude;
longi = position.coords.longitude;
map = L.map('map', {zoomControl: false}).setView([lati, longi], 15);
L.tileLayer('http://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', {maxZoom: 19,}).addTo(map);
L.control.scale({position: 'bottomright'}).addTo(map);
/*CARTODB LAYERS*/
var layerSource = {
user_name: 'YOUR USER NAME',
type: 'cartodb',
cartodb_logo: false,
sublayers: [{
sql: "SELECT * FROM winston_survey_tool WHERE point_class LIKE 'Orientation point'",
cartocss: '#winston_survey_tool{marker-fill:#D94C38;marker-opacity:1;line-color:#FFF;line-width:1;line-opacity:1;marker-allow-overlap:true; [zoom >= 15] {marker-width: 15} [zoom >= 19] {marker-width: 20}}'
},
{
sql: "SELECT * FROM winston_survey_tool WHERE point_class LIKE 'Survey point'",
cartocss: '#winston_survey_tool{marker-fill:#E0D03D;marker-opacity:1;line-color:#FFF;line-width:1;line-opacity:1;marker-allow-overlap:true; [zoom >= 15] {marker-width: 15} [zoom >= 19] {marker-width: 20}}'
}]
};
// STORE SUBLAYERS
var sublayers = [];
// ADD LAYER TO MAP
cartodb.createLayer(map,layerSource)
.addTo(map)
.done(function(layer) {
// SEPARATE THE SUBLAYERS
for (i = 0; i < layer.getSubLayerCount(); i++) {
sublayers[i] = layer.getSubLayer(i);
sublayers[i].hide();
};
// BUTTONS
$('#orientationCheck').click(function () {
orientationValue = $("#orientationCheck").val();
var query = "SELECT * FROM winston_survey_tool WHERE date LIKE'%";
yearSelectVal = $("#yearSelect").val();
query = query + yearSelectVal + "' AND point_class LIKE 'Orientation point'";
sublayers[0] = sublayers[0].setSQL(query);
if(orientationValue=="ON"){
sublayers[0].hide();
$('#orientationCheck').val("OFF");
$("#orientationCheck").addClass("off");
}
else{
sublayers[0].show();
$('#orientationCheck').val("ON");
$("#orientationCheck").removeClass("off");
};
});
$('#surveyCheck').click(function () {
surveyValue = $("#surveyCheck").val();
var query = "SELECT * FROM winston_survey_tool WHERE date LIKE'%";
yearSelectVal = $("#yearSelect").val();
query = query + yearSelectVal + "' AND point_class LIKE 'Survey point'";
sublayers[1] = sublayers[1].setSQL(query);
if(surveyValue=="ON"){
sublayers[1].hide();
$('#surveyCheck').val("OFF");
$("#surveyCheck").addClass("off");
}
else{
sublayers[1].show();
$('#surveyCheck').val("ON");
$("#surveyCheck").removeClass("off");
};
});
});

Leaflet API GeoJSON Error

I am trying to load a GeoJSON file for an interactive map of West Virginia Counties but I am getting an error saying Uncaught ReferenceError: wvData is not defined on line 140. Can someone help me in figuring out how to resolve this? I am attaching my full code.
<html>
<head>
<title>Leaflet Interactive GeoJSON</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.css" />
<style>
#map {
width: 100%;
height: 100%;
}
.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>
</head>
<body>
<div id="map"></div>
<script src="http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.js"></script>
<script type="text/javascript" src="CountySelection.js"></script>
<script type="text/javascript">
var map = L.map('map').setView([37.8, -96], 4);
L.tileLayer('https://{s}.tiles.mapbox.com/v3/{id}/{z}/{x}/{y}.png', {
maxZoom: 16,
attribution: 'Map data © OpenStreetMap contributors, ' +
'CC-BY-SA, ' +
'Imagery © Mapbox',
id: 'examples.map-20v6611k'
}).addTo(map);
// control that shows state info on hover
var info = L.control();
info.onAdd = function (map) {
this._div = L.DomUtil.create('div', 'info');
this.update();
return this._div;
};
info.update = function (props) {
this._div.innerHTML = '<h4>US Population Density</h4>' + (props ?
'<b>' + props.NAME + '</b><br />' + props.POP2010 + ' people / mi<sup>2</sup>'
: 'Hover over a state');
};
info.addTo(map);
// get color depending on population density value
function getColor(d) {
return d > 1000 ? '#800026' :
d > 500 ? '#BD0026' :
d > 200 ? '#E31A1C' :
d > 100 ? '#FC4E2A' :
d > 50 ? '#FD8D3C' :
d > 20 ? '#FEB24C' :
d > 10 ? '#FED976' :
'#FFEDA0';
}
function style(feature) {
return {
weight: 2,
opacity: 1,
color: 'white',
dashArray: '3',
fillOpacity: 0.7,
fillColor: getColor(feature.properties.POP2010)
};
}
function highlightFeature(e) {
var layer = e.target;
layer.setStyle({
weight: 5,
color: '#666',
dashArray: '',
fillOpacity: 0.7
});
if (!L.Browser.ie && !L.Browser.opera) {
layer.bringToFront();
}
info.update(layer.feature.properties);
}
var geojson;
function resetHighlight(e) {
geojson.resetStyle(e.target);
info.update();
}
function zoomToFeature(e) {
map.fitBounds(e.target.getBounds());
}
function onEachFeature(feature, layer) {
layer.on({
mouseover: highlightFeature,
mouseout: resetHighlight,
click: zoomToFeature
});
} <!-- ADDED: Line 140 in the source code -->
geojson = L.geoJson(wvData, {
style: style,
onEachFeature: onEachFeature
}).addTo(map);
map.attributionControl.addAttribution('Population data © US Census Bureau');
var legend = L.control({position: 'bottomright'});
legend.onAdd = function (map) {
var div = L.DomUtil.create('div', 'info legend'),
grades = [0, 10, 20, 50, 100, 200, 500, 1000],
labels = [],
from, to;
for (var i = 0; i < grades.length; i++) {
from = grades[i];
to = grades[i + 1];
labels.push(
'<i style="background:' + getColor(from + 1) + '"></i> ' +
from + (to ? '–' + to : '+'));
}
div.innerHTML = labels.join('<br>');
return div;
};
legend.addTo(map);
</script>
</body>
</html>
In the line
geojson = L.geoJson(wvData, {
style: style,
onEachFeature: onEachFeature
}).addTo(map);
you haven't yet defined wvData anywhere, and looks like you haven't loaded your geojson file yet either. If you're data is defined in a separate geojson file, you'll need to load it via an ajax request, or checkout the leaflet-omnivore plugin.
Lyzi Diamond published some blog posts about loading geojson with leaflet. It helped me a lot: http://lyzidiamond.com/posts/external-geojson-and-leaflet-the-other-way/