Rendering points with Leaflet from a `json.gz` file - javascript

I am using the SimpleHTTPServer for Python to display a basic Leaflet map.
I wish to create a series of points on the map, using a json.gz file as input.
The contents of this file (input.json.gz) look like this:
[[25.0153,55.45758],[25.01767,55.45569],[25.02065,55.45327],[25.02655,55.44846]]
My hardcoded code looks like this:
var pointA = new L.LatLng(25.146619, 55.225746);
var pointB = new L.LatLng(25.198696, 55.269794);
How do I read the json.gz file in javascript and iterate across it creating points? Ultimately I will collect these points into different pointLists and from this render a polyline. Full code below:
<!DOCTYPE html>
<html>
<head>
<title>Leaflet Web Map</title>
<!-- reference to Leaflet CSS -->
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.css" />
<!-- reference to Leaflet JavaScript -->
<script src="http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.js"></script>
<!-- set width and height styles for map -->
<style>
html, body, #map {
height: 100%;
width: 100%;
}
/* css to customize Leaflet default styles */
.custom .leaflet-popup-tip,
.custom .leaflet-popup-content-wrapper {
background: #e93434;
color: #ffffff;
}
</style>
</head>
<body>
<!-- place holder for map -->
<div id="map"></div>
<script>
// create map object, tell it to live in 'map' div and give initial latitude, longitude, zoom values
var map = L.map('map', {scrollWheelZoom:false}).setView([25.198696, 55.269794], 15);
// add base map tiles from OpenStreetMap and attribution info to 'map' div
L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors'
}).addTo(map);
var pointA = new L.LatLng(25.146619, 55.225746);
var pointB = new L.LatLng(25.198696, 55.269794);
var pointList = [pointA,pointB];
L.polyline(pointList,{icon: test_icon,color:'red','weight':10}).bindPopup(customPopup,customOptions).addTo(map);
</script>
</body>
</html>
EDIT
I have now reformatted my data into geojson format. Here is an exert:
{
"type": "FeatureCollection",
"features": [
{
"geometry": {
"type": "LineString",
"coordinates": [
[
25.1167,
55.19259
],
[
25.11285,
55.18714
],
[
25.11141,
55.1851
],
[
25.11077,
55.18422
],
[
25.11001,
55.18311
],
[
25.10855,
55.18105
],
[
25.10722,
55.17914
],
[
25.10644,
55.17805
],
[
25.10572,
55.17712
]
]
},
"type": "Feature",
"properties": {
"url": "blah",
"name": "blue"
}
},
I then try and load it using AJAX.
var geojsonLayer = new L.GeoJSON.AJAX("polylines.geojson");
geojsonLayer.addTo(map);
I've also added <script src="./polylines.geojson" type="text/javascript"></script> to the top of the html file.
However, this returns file not found. I've the file located in the same directory as index.html
code 404, message File not found
127.0.0.1 - - [13/Mar/2017 14:52:40] "GET polylines.geojson HTTP/1.1" 404 -

Related

Functionally setting Leaflet marker colors by a property for geojson data

Similarly to this question, we want to functionally set our Leaflet map's marker's colors based off a value, except our map is built with geojson data.
We want markers' colors to be functional to color_value (eg. different shade of green), but we don't know how to build that like this answer explains with L.AwesomeMarkers.icon({markerColor: determineColor(rating)});, because the markers for all our entries are built at once with pointToLayer: function (feature, coordinates) {return L.marker(coordinates, { icon: customicon });}. Simplified eg:
<!DOCTYPE html>
<style>
html,
body { margin: 0px; height: 100%; }
body {display: flex;}
</style>
<body>
<link rel="stylesheet" href="https://unpkg.com/leaflet#1.7.1/dist/leaflet.css"
integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A=="
crossorigin="" />
<script src="https://unpkg.com/leaflet#1.7.1/dist/leaflet.js"
integrity="sha512-XQoYMqMTK8LvdxXYG3nZ448hOEQiglfqkJs1NOQV44cWnUrBc8PkAOcXy20w0vlaXaVUearIOBhiXZ5V3ynxwA=="
crossorigin=""></script>
<div id="mapid" style="flex: 1"></div>
<script>var myMap = L.map("mapid");
L.tileLayer('https://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png').addTo(myMap);
myMap.setView([51.5, -0.09], 10);
var geojsonFeatureCollection =
{
"type": "FeatureCollection",
"features": [{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [-0.09, 51.5]
},
"properties": {
"color_value": 1
}
},
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [-0.4, 51.5]
},
"properties": {
"color_value": 10
}
}]
}
const myCustomColor = '#007a83'
const markerHtmlStyles = `
background-color: ${myCustomColor}; width: 1.3rem; height: 1.3rem; display: block;`
const customicon = L.divIcon({html: `<span style="${markerHtmlStyles}" />`})
L.geoJSON(geojsonFeatureCollection, {
pointToLayer: function (feature, coordinates) {
return L.marker(coordinates, { icon: customicon });
}
})
.addTo(myMap);
</script>
</body>
</html>
We've found this tutorial which gets us a little closer by explaining how to choose different markers based off some value, but we want to set the color itself, not choose from existing markers.
How can we make myCustomColor functional to color_value, or otherwise set marker colors programmatically for geojson data?
You are almost there! :-)
You can define the desired color directly as a property on each GeoJSON feature. Leaflet passes each feature of the GeoJSON collection to pointToLayer, allowing you to access its properties.
Check out the snippet below.
var myMap = L.map("mapid");
L.tileLayer('https://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png').addTo(myMap);
myMap.setView([51.5, -0.09], 10);
var geojsonFeatureCollection = {
"type": "FeatureCollection",
"features": [{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [-0.09, 51.5]
},
"properties": {
// You can define a different color for each property
// and access it in pointToLayer().
"color_value": '#007a83'
}
},
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [-0.4, 51.5]
},
"properties": {
// Here I might use a different color
"color_value": '#003134'
}
}
]
};
L.geoJSON(geojsonFeatureCollection, {
pointToLayer: function(feature, coordinates) {
// Leaflet passes each GeoJSON feature in the collection in here,
// allowing you to access the feature's properties
const color_value = feature.properties.color_value;
// With the color obtained from the properties, you can now create
// the marker icon with the correct color.
const markerHtmlStyles = `background-color: ${color_value}; width: 1.3rem; height: 1.3rem; display: block;`;
const customicon = L.divIcon({
html: `<span style="${markerHtmlStyles}" />`
});
return L.marker(coordinates, {
icon: customicon
});
},
})
.addTo(myMap);
html,
body {
margin: 0px;
height: 100%;
}
body {
display: flex;
}
<!DOCTYPE html>
<body>
<link rel="stylesheet" href="https://unpkg.com/leaflet#1.7.1/dist/leaflet.css" integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A==" crossorigin="" />
<script src="https://unpkg.com/leaflet#1.7.1/dist/leaflet.js" integrity="sha512-XQoYMqMTK8LvdxXYG3nZ448hOEQiglfqkJs1NOQV44cWnUrBc8PkAOcXy20w0vlaXaVUearIOBhiXZ5V3ynxwA==" crossorigin=""></script>
<div id="mapid" style="flex: 1"></div>
</body>
</html>
Alternatively, if you want to calculate the color based off color_value, you can proceed almost the same way: Read color_value property inside pointToLayer, generate the color (e.g., by using an approach like the one outlined here: Programmatically Lighten or Darken a hex color (or rgb, and blend colors)) and pass it to your marker's styling.
Hope this helps!

How to put a title on a leaflet geoJSON?

I have a geoJSON object that is added to the map like this:
L.geoJSON(seine_mar).addTo(map)
Which creates the following:
I would like to add a text on hover of this zone. It is possible with markers, like this for example:
L.marker([value.lat, value.lon], { title: "Hi there"}).addTo(map);
How could I achieve the same with the geoJSON, resulting in displaying a text on hover ? According to the docs, there is no such property as title or label.
Example of desired result (fear my paint skills):
Try using bindTooltip() with permanent option
https://leafletjs.com/reference-1.7.1.html#tooltip
var map = L.map('map').setView([39.74739, -105], 13);
L.tileLayer('https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token=pk.eyJ1IjoibWFwYm94IiwiYSI6ImNpejY4NXVycTA2emYycXBndHRqcmZ3N3gifQ.rJcFIG214AriISLbB6B5aw', {
maxZoom: 18,
attribution: 'Map data © OpenStreetMap contributors, ' +
'Imagery © Mapbox',
id: 'mapbox/light-v9',
tileSize: 512,
zoomOffset: -1
}).addTo(map);
var campus = {
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[
[-105.00432014465332, 39.74732195489861],
[-105.00715255737305, 39.74620006835170],
[-105.00921249389647, 39.74468219277038],
[-105.01067161560059, 39.74362625960105],
[-105.01195907592773, 39.74290029616054],
[-105.00989913940431, 39.74078835902781],
[-105.00758171081543, 39.74059036160317],
[-105.00346183776855, 39.74059036160317],
[-105.00097274780272, 39.74059036160317],
[-105.00062942504881, 39.74072235994946],
[-105.00020027160645, 39.74191033368865],
[-105.00071525573731, 39.74276830198601],
[-105.00097274780272, 39.74369225589818],
[-105.00097274780272, 39.74461619742136],
[-105.00123023986816, 39.74534214278395],
[-105.00183105468751, 39.74613407445653],
[-105.00432014465332, 39.74732195489861]
]
]
}
};
L.geoJSON(campus)
.bindTooltip('Hi there', {
permanent: true,
direction: 'center'
})
.addTo(map);
<link rel="stylesheet" href="https://unpkg.com/leaflet#1.7.1/dist/leaflet.css" integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A==" crossorigin="" />
<script src="https://unpkg.com/leaflet#1.7.1/dist/leaflet.js" integrity="sha512-XQoYMqMTK8LvdxXYG3nZ448hOEQiglfqkJs1NOQV44cWnUrBc8PkAOcXy20w0vlaXaVUearIOBhiXZ5V3ynxwA==" crossorigin=""></script>
<div id='map'></div>
You can use the onEachFeature function of L.geoJSON() to loop through all layers and then add a Tooltip with layer.bindTooltip("HI") to it.
L.geoJSON(seine_mar,{
onEachFeature: function(feature, layer){
layer.bindTooltip('Hi there', {permanent: true}).openTooltip();
// or over a feature property layer.bindTooltip(feature.properties.customTitle)
}
}).addTo(map)

Input data given to 'drone' is not a valid GeoJSON object

I am trying to render a polygon from a webservice to a mapbox map as a proof of concept.
My webservice gives me the following geojson with some mock data:
{"type":"FeatureCollection","features":[{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[9.750441312789917,52.350087018909683],[9.7523081302642822,52.34896634765532],[9.7523403167724609,52.350106679555289],[9.750441312789917,52.350087018909683]]]},"properties":{"test1":"value1"}}]}
But mapbox gives me the error I can see in the browser console:
Input data given to 'drone' is not a valid GeoJSON object.
http://geojson.io and http://geojsonlint.com/ understand that geojson my webservice was generating.
It is send as content-type "text/plain; charset=utf-8"
because as application/json it gets a lot of \ characters, which is not working.
Below you can see the html code I am using for this test.
So what am I doing wrong here?
I checked that the geojson is correctly formatted and I added the same geojson directly as a source and it worked. See below.
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8' />
<title>Add multiple geometries from one GeoJSON source</title>
<meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
<script src='https://api.tiles.mapbox.com/mapbox-gl-js/v1.2.0/mapbox-gl.js'></script>
<link href='https://api.tiles.mapbox.com/mapbox-gl-js/v1.2.0/mapbox-gl.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>
mapboxgl.accessToken = 'pk.xxxxxx';
var map = new mapboxgl.Map({
container: "map",
style: "mapbox://styles/mapbox/streets-v11",
center: [9.754206, 52.355394],
zoom: 14
});
var url = 'https://localhost:44337/api/resource';
map.on('load', function () {
map.addSource('drone', { type: 'geojson', data: url });
map.addLayer({
"id": "drone",
"type": "fill",
"source": "drone",
'layout': {},
'paint': {
'fill-color': '#088',
'fill-opacity': 0.8
}
});
});
</script>
</body>
</html>
I expect that there is some polygon on the map, like it is if I put the geojson directly into the code:
map.on('load', function () {
map.addLayer({
'id': 'maine',
'type': 'fill',
'source': {
'type': 'geojson',
'data':
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
9.750441312789917,
52.350087018909683
],
[
9.7523081302642822,
52.34896634765532
],
[
9.7523403167724609,
52.350106679555289
],
[
9.750441312789917,
52.350087018909683
]
]
]
},
"properties": {
"test1": "value1"
}
}
]
}
},
'layout': {},
'paint': {
'fill-color': '#088',
'fill-opacity': 0.8
}
});
});
Maybe you should try to fetch your geoJSON from your API manually and see if that works, passing a URL as a geoJSON object to mapbox seems to work as well, but maybe something is wrong with your API, so I would do a manual fetch and see if anything goes wrong. Furthermore, you should definitely change your content-type header back to application/json, the following snippet assumes you did that!
map.on('load', async function() {
let response = await fetch(url);
let data = await (
response.headers.get('content-type').includes('json')
? response.json() // this will parse your JSON if the proper content-type header is set!
: response.text()
);
map.addSource('drone', { type: 'geojson', data: data });
map.addLayer({
"id": "drone",
"type": "fill",
"source": "drone",
'layout': {},
'paint': {
'fill-color': '#088',
'fill-opacity': 0.8
}
});
});
Ok, I reckon mapboxgl is doing call this, but if it helps, this is the asp.net core backend snippet:
// GET: api/resource
[HttpGet]
public JsonResult GetResources()
{
var poly = _context.Resources.First().Polygon;
AttributesTable attributes = new AttributesTable();
attributes.Add("test1", "value1");
IFeature feature = new Feature(poly, attributes);
FeatureCollection featureCollection = new FeatureCollection(new Collection<IFeature> { feature });
var gjw = new GeoJsonWriter();
gjw.SerializerSettings.NullValueHandling = NullValueHandling.Ignore;
string actual = gjw.Write(featureCollection);
return new JsonResult(actual);
}
I am using NetTopologySuite.
And poly is a SQL Server Geography type Polygon.
EDIT: Ok I figured it out myself:
I had to do this:
return Content(actual, "application/json", Encoding.UTF8);
and use ContentResult as a return type.
My Json seemed to get serialized twice, since it got too many characters.
Ah and the fetch and response.json() parsing exside proposed is not needed with the correct geojson response. But it is also working with it. ;)
Thanks though.

D3 overlay on google maps not fully rendering

as background, I've been working on a small project highlighting local neighborhoods and crime incidents over time in a d3 overlay on a google map, but finding that my overlay only partially renders.
The initial objective is achieving something visually like the map rendered here.
The code for the this target/example map can be seen in the JSfiddle here. I've used this example to guide my current work.
Unfortunately, when I run my overlay code with a google map for the view I'm targeting, I find that only small portions of the overall d3 paths actually render on the google map. From debugging in the console, I can see that all the relevant geojson data is loading and each path array is being included as a path node, but only one or two of the paths is being displayed on the map - e.g. I only see transparent fill over one or two neighborhoods (as shown in image linked below).
View of map's partial overlay rendering
Can anyone advise on why the d3 overlay with geojson data isn't being fully displayed, and how I can fix this, so that I see the full neighborhoods overlay?
Code below, and many thanks in advance.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1.0, user-scalable=no"/>
<script type="text/javascript" src="http://maps.google.com/maps/api/js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script type="text/javascript">
function draw(geo_data) {
"use strict";
var margin = 100,
width = 1400 - margin,
height = 1000 - margin;
var overlay = new google.maps.OverlayView();
overlay.onAdd = function() {
var layer = d3.select(this.getPanes().overlayLayer).append("div")
.attr("class", "SvgOverlay");
var svg = layer.append("svg");
var neighborhoods = svg.append("g").attr("class", "Neighborhoods");
overlay.draw = function(){
var markerOverlay = this;
var overlayProjection = markerOverlay.getProjection();
var googleMapProjection = function (coordinates) {
var googleCoordinates = new google.maps.LatLng(coordinates[1], coordinates[0]);
var pixelCoordinates = overlayProjection.fromLatLngToDivPixel(googleCoordinates);
return [pixelCoordinates.x, pixelCoordinates.y];
};
path = d3.geo.path().projection(googleMapProjection);
neighborhoods.selectAll("path")
.data(geo_data.features)
.enter()
.append("path")
.attr("d", path);
};
};
overlay.setMap(google_base);
};
</script>
</head>
<body>
<div id="google_map">
</div>
<script type="text/javascript">
var google_base = new google.maps.Map(d3.select("#google_map").node(), {
zoom: 13,
center: new google.maps.LatLng(37.81, -122.26),
mapTypeId: google.maps.MapTypeId.ROADMAP,
disableDefaultUI: true,
zoomControl: false,
styles:[{"stylers": [{"saturation": -50},{"lightness": 75}]}]
});
d3.json("neighborhoods.geojson", draw);
</script>
</body>
</html>
Note: example geojson data
{
"type": "FeatureCollection",
"features": [
{ "type": "Feature", "properties": { "NAME": "Montclair" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -122.21594888924534, 37.832181779651613 ], [ -122.199005320235656, 37.839802502024263 ], [ -122.195700218341074, 37.834717626248832 ], [ -122.204865526096057, 37.83063877524836 ], [ -122.204065506384751, 37.826138894083272 ], [ -122.203989505503827, 37.825037923657973 ], [ -122.204862534763905, 37.82484093266519 ], [ -122.206357582839033, 37.825825912182573 ], [ -122.207037605809575, 37.825554922390438 ], [ -122.21045471226752, 37.829664825068207 ], [ -122.212788791761739, 37.828300871884338 ], [ -122.21104273740049, 37.826278919502776 ], [ -122.209740697282683, 37.824438964026321 ], [ -122.209719696572449, 37.824446963720987 ], [ -122.207737635353567, 37.821578033328542 ], [ -122.207938642362265, 37.821377039630789 ], [ -122.208037645976759, 37.82117704547791 ], [ -122.208238653148413, 37.820877054469094 ], [ -122.210140719787248, 37.818876116804304 ], [ -122.211442762016475, 37.819577103232504 ], [ -122.212243787852969, 37.820077093014056 ], [ -122.214446859285928, 37.821177072378525 ], [ -122.215348888403369, 37.821678062551726 ], [ -122.216549926889712, 37.822478045852897 ], [ -122.216749933170547, 37.822678041257156 ], [ -122.217351952809793, 37.822878038347341 ], [ -122.218401986194451, 37.823679020982773 ], [ -122.221157073570424, 37.825778975452437 ], [ -122.222459113734274, 37.827279940103317 ], [ -122.222759122864446, 37.827679930485409 ], [ -122.223861155874616, 37.829379888880801 ], [ -122.224462173475956, 37.830480861460941 ], [ -122.223133128544632, 37.830996841877365 ], [ -122.222659112528717, 37.83118083489429 ], [ -122.221358066237769, 37.83288178322033 ], [ -122.219555006623537, 37.832981772972666 ], [ -122.218852983299286, 37.833081767323314 ], [ -122.217851950689095, 37.832881768578396 ], [ -122.217351934210413, 37.832881766490274 ], [ -122.21594888924534, 37.832181779651613 ] ] ] } }

using Google Map Javascript API to render a GeoJSON Polygon overlay from JavaScript Variable

I am trying to render a GeoJSON polygon from a variable called data using the Google Javascript API. The overlay does not render properly and I do not know why. Can somebody please show me how to render a polygon on the map?
<!DOCTYPE html>
<html>
<head>
<script src="http://maps.googleapis.com/maps/api/js"></script>
<script>
var x=new google.maps.LatLng(40.75597,-73.974228);
function initialize() {
var data = { "type" : "Polygon", "coordinates" : [ [ [ -73.974228, 40.75597 ], [ -73.983841, 40.742931 ], [ -74.008133, 40.75307500000001 ], [ -73.998131, 40.765915 ], [ -73.974228, 40.75597 ] ] ] }
var mapProp = {
center:x,
zoom:4,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var map=new google.maps.Map(document.getElementById("googleMap"),mapProp);
map.data.loadGeoJson(data);
}
google.maps.event.addDomListener(window, 'load', initialize);
</script>
</head>
<body>
<div id="googleMap" style="width:500px;height:380px;"></div>
</body>
</html>
I get a javascript error with your code: Uncaught InvalidValueError: not a Feature or FeatureCollection. Make your polygon a "Feature":
var data = {
type: "Feature",
geometry: {
"type": "Polygon",
"coordinates": [
[
[-73.974228, 40.75597],
[-73.983841, 40.742931],
[-74.008133, 40.75307500000001],
[-73.998131, 40.765915],
[-73.974228, 40.75597]
]
]
}
};
working fiddle
code snippet:
function initialize() {
var data = {
type: "Feature",
geometry: {
"type": "Polygon",
"coordinates": [
[
[-73.974228, 40.75597],
[-73.983841, 40.742931],
[-74.008133, 40.75307500000001],
[-73.998131, 40.765915],
[-73.974228, 40.75597]
]
]
}
};
var mapProp = {
center: new google.maps.LatLng(40.75597, -73.974228),
zoom: 12,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var map = new google.maps.Map(document.getElementById("googleMap"), mapProp);
map.data.addGeoJson(data);
}
google.maps.event.addDomListener(window, 'load', initialize);
html, body, #googleMap {
width: 100%;
height: 100%;
}
<script src="https://maps.google.com/maps/api/js?key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk"></script>
<div id="googleMap"></div>
Firstly, geojson object must be a Feature or FeatureCollection.
Secondly, use:
map.data.addGeoJson(data) to load geojson from variable
instead of:
map.data.loadGeoJson(data) for loading geojson from .json files
More info in Google Maps API

Categories