When loading a geoJSON file into a Google Map as a data layer, how does one access the properties of the data layer itself?
I know how to access the individual properties, like posts_here in the below example. What I'm looking to get is the properties for the layer itself- in this example, maxPosts.
$.getJSON("http://example.com/posts/grid.json" + location.search, function (data) {
grid = map_canvas.data.addGeoJson(data);
map_canvas.data.setStyle(function(feature) {
return /** #type {google.maps.Data.StyleOptions} */({
strokeWeight: Math.log(feature.getProperty('posts_here')),
});
})
});
Example of the grid.json I'm loading:
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[
[-58,-35],
[-58,-34],
[-57,-34],
[-57,-35],
[-58,-35]
]
]
},
"properties": {
"posts_here": "177"
}
}
],
"properties": {
"maxPosts": "177"
}
}
The API does only parse the features-array of a FeatureCollection, when you want to access additional properties you must implement it on your own.
Based on the given code it isn't complicated, because the geoJson is accessible as object via data inside the $.getJSON-callback, you may simply access the property via
data.properties.maxPosts
I believe you should be able to use getFeatureById(id:number|string) to access information you've added from your geoJSON.
Related
I am have a vuejs/nuxtjs application using the gmap-vue package which is a fork of vue-google-maps. I have followed the drawing manager with slot example which is working as shown on the document, good times!
But...
I would like to do two more things
Save the data that I added to the map, how do I access it?
How to load geojson onto the map and then edit it.
Examle below:
More information from further investigation.
I am trying to build up data in the form of geojson
let shapes = [];
for(let shape in this.shapes){
let tmp = {
"type": "Feature",
"properties": {
"id": this.shapes[shape].id || null,
"zIndex": this.shapes[shape].zIndex || null
},
"geometry": {
"type": this.shapes[shape].type,
"coordinates": // where to find shape coordinates?
}
};
shapes.push(tmp)
}
console.log(JSON.stringify(shapes));
displays the following in the console.log
[
{
"type":"Feature",
"properties":{
"id":null,
"zIndex":null
},
"geometry":{
"type":"polygon",
"coordinates": // where to find coordinates?
}
}
]
console.log[this.shapes[shape]];
returns
{__ob__: Observer}
overlay: (...)
type: (...)
How can I access the coordinates?
For anyone else looking for an answer, I hope this helps you out.
const newShapes = [];
this.shapes.forEach((shape) => {
const coords = [];
shape.overlay.latLngs.getArray().forEach((latLng) => {
coords.push([latLng.lat, latLng.lng]);
});
newShapes.push({
type: 'Feature',
geometry: {
type: shape.type,
coordinates: coords,
},
});
});
As shown here: https://diegoazh.github.io/gmap-vue/#getting-a-map-reference
this.$refs.mapRef.$mapPromise.then((map) => {
map.panTo({lat: 1.38, lng: 103.80})
})
is working pretty great.
<gmap-drawing-manager ref="drawingRef">
and
<gmap-drawing-manager :shapes="shapes" ref="drawingRef">
allows to access the same information with this.shapes!
Problem:
I am working on project where I use node.js server that communicate with my spatial DB in PG and my client uses mapbox to vizualize map on his side. After hitting a button, the request is send to server, server to psql, psql to server as result query and then through socket.io back to client, where I want to put my geoJSON / new geometry as new layer on his map after that client buttonclick occurs. Map on client side in HTML is well working and I can interact with it. I use JS inside of HTML page of my client. From there I need to update mapbox map with new geometry after button click.
Code sample:
But I tried this code, but this do nothing after button click and will show no errors in devTool Chrome console:
<script>
mapboxgl.accessToken = 'secretToken-I-have-just-for-ilustr--this-is-working';
var map = new mapboxgl.Map({
container: 'map', // container id
style: 'mapbox://styles/mapbox/streets-v10',
center: [17.10, 48.14], // starting position on Bratislava
zoom: 11 // starting zoom
});
// Add zoom and rotation controls to the map.
map.addControl(new mapboxgl.NavigationControl());
// later SOCKET PROCESSING
$(document).ready(function(){
$('#buttonRun').click(function(e){
map.on('load', function () {
alert("got HERE") // this is working, alert shows itself
map.addLayer({
"id": "route",
"type": "line",
"source": {
"type": "geojson",
"data": {
"type": "Feature",
"properties": {},
"geometry": {
"type": "LineString",
"coordinates": [
[-122.48369693756104, 37.83381888486939],
[-122.48348236083984, 37.83317489144141],
[-122.48339653015138, 37.83270036637107],
[-122.48356819152832, 37.832056363179625],
[-122.48404026031496, 37.83114119107971],
[-122.48404026031496, 37.83049717427869],
[-122.48348236083984, 37.829920943955045],
[-122.48356819152832, 37.82954808664175],
[-122.48507022857666, 37.82944639795659],
[-122.48610019683838, 37.82880236636284],
[-122.48695850372314, 37.82931081282506],
[-122.48700141906738, 37.83080223556934],
[-122.48751640319824, 37.83168351665737],
[-122.48803138732912, 37.832158048267786],
[-122.48888969421387, 37.83297152392784],
[-122.48987674713133, 37.83263257682617],
[-122.49043464660643, 37.832937629287755],
[-122.49125003814696, 37.832429207817725],
[-122.49163627624512, 37.832564787218985],
[-122.49223709106445, 37.83337825839438],
[-122.49378204345702, 37.83368330777276]
]
}
}
},
"layout": {
"line-join": "round",
"line-cap": "round"
},
"paint": {
"line-color": "#888",
"line-width": 8
}
});
});
});
});
</script>
Even this is not working - even if I set data in click function in a static way, but this data will later change dynamically. If I add that layer out of click event function scope, it is working and layer loads on client map.
Settings / versions:
Windows10 Pro - 64-bit
Google Chrome - Version 69.0.3497.100 (Official Build) (64-bit)
Node.js - v10.11.0
mapbox-gl.js v0.49.0
Q:
Is there any way how to add layer to mapbox map dynamically, please? And later to remove without page refresh? (I still not found answer, even here)
Solution
Okay, I found something I before did not see, concretely this.
I better read documentation, and it is impossible to setup new layers dynamically, but now it is working as follows / you need to:
Out of all scopes define your variables, for example geoJson1, and geoJson2 that you can later edit / fill with function
Setup your layer in advance on map with your ID (as in code below) and fill it for example with goeJson1 or with empty []
In your on click listener function call this: map.getSource('data-update').setData(geojson2);
You just can setup as many layers in advance as you need, and later you can update them.
Code result:
<script>
mapboxgl.accessToken = 'token-from-your-registered-account';
var map = new mapboxgl.Map({
container: 'map', // container id
style: 'mapbox://styles/mapbox/streets-v10',
center: [17.10, 48.14], // starting position on Bratislava
zoom: 11 // starting zoom
});
var geojson = {
"type": "FeatureCollection",
"features": [{
"type": "Feature",
"geometry": {
"type": "LineString",
"coordinates": [
[-122.48369693756104, 37.83381888486939],
[-122.48348236083984, 37.83317489144141],
[-122.48339653015138, 37.83270036637107],
[-122.48356819152832, 37.832056363179625],
[-122.48404026031496, 37.83114119107971]
]
}
}]
};
var geojson2 = {
"type": "FeatureCollection",
"features": [{
"type": "Feature",
"geometry": {
"type": "LineString",
"coordinates": [
[-122.48369693756104, 37.83381888486939],
[-122.48348236083984, 37.83317489144141],
[-122.48339653015138, 37.83270036637107],
[-122.48356819152832, 37.832056363179625],
[-122.48404026031496, 37.83114119107971],
[-122.48404026031496, 37.83049717427869],
[-122.48348236083984, 37.829920943955045],
[-122.48356819152832, 37.82954808664175],
[-122.48507022857666, 37.82944639795659],
[-122.48610019683838, 37.82880236636284],
[-122.48695850372314, 37.82931081282506],
[-122.48700141906738, 37.83080223556934],
[-122.48751640319824, 37.83168351665737],
[-122.48803138732912, 37.832158048267786],
[-122.48888969421387, 37.83297152392784],
[-122.48987674713133, 37.83263257682617],
[-122.49043464660643, 37.832937629287755],
[-122.49125003814696, 37.832429207817725],
[-122.49163627624512, 37.832564787218985],
[-122.49223709106445, 37.83337825839438],
[-122.49378204345702, 37.83368330777276]
]
}
}]
};
map.on('load', function () {
map.addLayer({
"id": "data-update",
"type": "line",
"source": {
"type": "geojson",
"data": geojson // your previously defined variable
},
"layout": {
"line-join": "round",
"line-cap": "round"
},
"paint": {
"line-color": "#888",
"line-width": 8
}
});
});
$(document).ready(function(){
$('#buttonRun').click(function(e){
map.getSource('data-update').setData(geojson2);
});
});
</script>
I'm working on an interactive map showing the history of rail transport in a city using Leaflet and some GeoJSON data. My GeoJSON data looks like this:
var lines = [
{
"type": "FeatureCollection",
"name": "stadtmitte_gerresheim",
"startYear": "1902",
"endYear": "2019",
"lineColor": "#DD0000",
"trainID": "001",
"features": [
{
"type": "Feature",
"properties": {
"lineName": "U73",
"station1": "Universität Ost/Botanischer Garten",
"station2": "Gerresheim S",
"startYear": "2016",
"endYear": "2019",
"lineColor": "#DD0000",
},
"geometry": {
"type": "GeometryCollection",
"geometries": [
{
"type": "LineString",
"lineID": "001",
"lineDescription": "Schleife am S-Bahnhof Gerresheim",
"coordinates": [...
]
},
{
"type": "LineString",
"lineID": "002",
"lineDescription": "Gerresheim S bis Ecke Schönaustraße/Heyestraße",
"coordinates": [...
]
}
....
I'm working with several FeatureCollections, that consist of some Features. As you can see the geometry of each feature is defined by a GeometryCollection (that mostly comprises LineStrings).
I'd like to use Leaflet to style each single geometry (the LineStrings from the GeometryCollections) according to its members. At the moment I only manage to style the whole GeometryCollection via the Feature:
L.geoJSON(route, {
filter: function(feature) {
if (checkYear(feature)) {
return true;
} else {
return false;
}
},
style: function(feature) {
switch (feature.properties.tunnel) {
case 'yes': return {color: "#ff0000", dashArray: "1,6"};
default: return {color: "#ff0000"}
}
},
pointToLayer: function (feature, latlng) {
getIconSize();
switch (feature.properties.underground) {
case 'yes': return L.marker(latlng, {icon: stadtbahnIcon});
default: return L.marker(latlng, {icon: strassenbahnIcon});
}
}
}).addTo(mymap);
}
My question: Is it possible to hand individual style functions to different Geometries of the same Feature instead of handing the same style function to all Geometries in this Feature? And if so, how? I guess it should be possible, as Leaflet creates a single path for every Geometry.
Cheers
Is it possible to hand individual style functions to different Geometries of the same Feature?
No.
Leaflet creates one instance of L.Layer per Feature in the GeoJSON, right over here; and the application of the style callback function just loops over the L.Layers corresponding to each feature, it doesn't drill down on nested L.LayerGroups (also, the L.Polylines and L.Polygons created as the result of a GeometryCollection don't have a reference to the original GeoJSON feature nor its properties).
I guess you'll want to use TurfJS' geomEach (or something similar) in order to preprocess your features&geometries.
I am trying to create an offline map through the following steps:
Download data from Natural Earth Data
Convert the shape files to GeoJSON
Add the GeoJSON files as layers in OpenLayers3
I am struggling to get the rivers right, I can display them, but only as a line with a fixed width. However, when looking at the file I created from Natural Earth Data I see that there are in fact many short lines, and each has a width (strokeweig) specified. See snippet below for illustration.
{
"type": "FeatureCollection",
"features": [
{ "type": "Feature", "properties": { "strokeweig": 0.20000000300000001, "scalerank": 5, "featurecla": "River", "name": null, "dissolve": "River_untitled_77", "note": "_untitled_77" }, "geometry": { "type": "LineString", "coordinates": [ [ -72.991327277300684, 46.177440090512803 ], [ -73.078557095009359, 46.160128485695026 ], [ -73.146304897744017, 46.123541571632373 ], [ -73.177181566038399, 46.070624904965499 ], [ -73.163952399371681, 46.044166571632061 ] ] } },
{ "type": "Feature", "properties": { "strokeweig": 0.149999991, "scalerank": 5, "featurecla": "River", "name": "Ebro", "dissolve": "RiverEbro", "note": null }, "geometry": { "type": "LineString", "coordinates": [ [ -4.188860236009816, 43.011173407557422 ], [ -4.10225053548865, 43.001484076502706 ], [ -4.054759894212424, 42.952520656906145 ], [ -4.017449510097691, 42.861053371749534 ], [ -3.96267249186829, 42.825034898442098 ], [ -3.890377163091955, 42.844413560551544 ], [ -3.821957566737524, 42.841855577153098 ], [ -3.757387864588821, 42.81728343359832 ], [ -3.70925126790894, 42.832631333988999 ], [ -3.677521938481732, 42.887899278325165 ], [ -3.626775681971111, 42.89800202083822 ], [ -3.52213090658006, 42.845447089197393 ] ] } },
....
]
So, my question is twofold:
How do I work with this types of data in the first place to display a line with a width as specified in the strokeweig property of a features array item?
How do I deal with this value when zooming in and out?
Thanks,
Hendrik
You can find an example here http://openlayersbook.github.io/ch06-styling-vector-layers/example-07.html that is very near to your needs.
There is not a built-in solution. I can imagine at least two ways: Instead of creating one layer for the whole rivers featurecollection, create one layer for each "scalerank", and set their min/maxResolution accordingly. Otherwise, you can listen to the MapView change:resolution event, and add/remove or show/hide the features accordingly.
The code snippet below does sort of what I wanted:
var layer = new ol.layer.Vector({
source: new ol.source.Vector({
url: 'maps/rivers.json',
format: new ol.format.GeoJSON()
}),
style: function(feature, resolution) {
var strokeWeight, scaleRank, width, style;
strokeWeight = feature.get('strokeweig');
scaleRank = feature.get('scalerank');
// strokeWeight is multiplied by scaleRanks because I think this is why this property was included in the GeoJSON
// This number is then corrected for different zoom levels, max-resolution~611.5 based on map.MaxZoom=18
// The calculation with dividers represents the amount of times the current resolution is SMALLER than
// the max-resolution. Note that the value 611.5 will need to be changed when we decide the
// map.maxZoom needs to be increased
width = strokeWeight * scaleRank * (611.5 / resolution));
style = new ol.style.Style({
stroke: new ol.style.Stroke({
color: 'rgb(35, 51, 60)',
width: width
})
});
return [style];
}
});
I am not super happy with the formula that computes the width. Obviously taking a static value is not ideal and I don't know how the properties actually should have been combined to produce the correct line width.
I think I will be looking into the solution that Francesco suggested to improve it. This still won't be ideal because the max scaleRank in the file is 6 and I will be allowing zoom up to 18 (I know it won't be a very detailed map but that's OK).
Many thanks to Francesco for his help!!!!!
I am trying to make an interactive map using MapBox.
The basic idea is that people will hover over certain regions and they will light up and an event will trigger showing some content.
I got the map set up using Mapbox and then went ahead and got some data for the points by first
Going here to create a shapefile - http://www.gadm.org/country
And then going here to convert that file into a json file - http://www.mapshaper.org/
I then load the data into the map via mapbox and here is my result! (code below) - http://dev.touch-akl.com/nzmap/
As you can see thanks to the data I have downloaded it is almost a perfect trace of the country the only problem is that all the regions seem to be ONE object when hovering.
$.ajax({
url: '/nzmap/js/nzmap.json',
dataType: 'json',
success: function load(data) {
// set up the regions based on the JSON data and then call the styles and the hovers
var regions = L.geoJson(data, {
style: getStyle,
onEachFeature: onEachFeature
}).addTo(map);
// selects used to style the regionsLayer
function getStyle(feature) {
return {
weight: 2,
opacity: 0.1,
color: 'black',
fillOpacity: 0.7,
fillColor: 'red'
};
}
// combine the mouse in and mouse out?
function onEachFeature(feature, layer) {
layer.on({
mousemove: mousemove,
mouseout: mouseout
});
}
// mouse over change the layer styles
function mousemove(e) {
var layer = e.target;
// highlight feature
layer.setStyle({
weight: 3,
opacity: 0.3,
fillOpacity: 0.9
});
if (!L.Browser.ie && !L.Browser.opera) {
layer.bringToFront();
}
}
//mouse out reset the style
function mouseout(e) {
regions.resetStyle(e.target);
}
}
});
Here is a link to the json file if that helps understand what is going on - http://dev.touch-akl.com/nzmap//js/nzmap.json
It looks like some of the polygons are 'multipolygons' and some are seperate. I managed to create a new version of the map using just one region and it looks like this - http://dev.touch-akl.com/nzmap2/
My function for doing this is much the same except I have split the region off and use a json file that only has the coordinates for that region.
This is the JSON file for that example - http://dev.touch-akl.com/nzmap2/js/waikato.json
So where I am stuck is... Is there a way to format the original json file in a way that will keep each region as it's own separate object for hovers? And if so how can I go about changing the way I am setting up the map get the desired result?
It looks like I could make a seperate json file for each region like I am doing with the second example but that just does not seem like the right way to do this, been stuck for days so any help would be appreciated!
This is because what you are using isn't proper GeoJSON, at least not the GeoJSON L.GeoJSON expects. What you need is a GeoJSON FeatureCollection. A collection would look something like this:
[{
"type": "Feature",
"properties": {
"foo": "bar"
},
"geometry": {
"type": "Polygon",
"coordinates": [[
[-104.05, 48.99],
[-97.22, 48.98],
[-96.58, 45.94],
[-104.03, 45.94],
[-104.05, 48.99]
]]
}
}, {
"type": "Feature",
"properties": {
"bar": "foo"
},
"geometry": {
"type": "Polygon",
"coordinates": [[
[-109.05, 41.00],
[-102.06, 40.99],
[-102.03, 36.99],
[-109.04, 36.99],
[-109.05, 41.00]
]]
}
}]
As you can see, a FeatureCollection contains individual features which can be or type Point, MultiPoint, LineString, MultiLineString, Polygon or MultiPolygon. If you use a FeatureCollection L.GeoJSON will work as you would expect. Take a look at the reference for FeatureCollection and L.GeoJSON. A tutorial/example on using them both can be found here.