I am currently trying to make a simple visualization using Mapbox that is based on additional data provided by a local geojson file. I cannot upload this file to Mapbox and would like to keep it local.
I have used this base code from Mapbox that I have modified to include a local geojson file that is structured like this:
{"features": [{"geometry": null, "location": {"coordinates": [40.730610, -73.935242], "type": "Point"}, "properties": {"X": "1", "group": "1"}, "type": "Feature"},...}
I have modified the example code from Mapbox so that it is now:
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8' />
<title>Style circles with a data-driven property</title>
<meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
<script src='https://api.tiles.mapbox.com/mapbox-gl-js/v0.48.0/mapbox-gl.js'></script>
<script type="text/javascript" src="http://code.jquery.com/jquery-1.7.1.min.js"></script>
<link href='https://api.tiles.mapbox.com/mapbox-gl-js/v0.48.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.eyJ1IjoibG9ubmliZXNhbmNvbiIsImEiOiJjamxjaWNpOHQwMHV0M3FwaHhneGhvY2l2In0.7GxI8W_dnTKITNF4hEvZeQ';
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/light-v9',
zoom: 12,
center: [-73.935242, 40.730610],
pitch: 20,
});
var url = "GeoObs.json.json"
map.on('load', function () {
var layers = map.getStyle().layers;
var labelLayerId;
for (var i = 0; i < layers.length; i++) {
if (layers[i].type === 'symbol' && layers[i].layout['text-field']) {
labelLayerId = layers[i].id;
break;
}
}
map.addSource("my_data", {
type: "geojson",
data: url //"./GeoObs.json",
/*cluster: true,
clusterMaxZoom: 15, // Max zoom to cluster points on
clusterRadius: 50 // Radius of each cluster when clustering points (defaults to 50)*/
});
map.addLayer({
'id': 'population',
'type': 'circle',
source: 'my_data',
'source-layer': 'my_data',
'paint': {
// make circles larger as the user zooms from z12 to z22
'circle-radius': {
'base': 1.75,
'stops': [[12, 2], [22, 180]]
},
// color circles by ethnicity, using a match expression
// https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-match
'circle-color': [
'match',
['get', 'group'],
'1', '#fbb03b',
'2', '#223b53',
'3', '#e55e5e',
'4', '#3bb2d0',
/* other */ '#ccc'
]
}
});
map.addLayer({
'id': '3d-buildings',
'source': 'composite',
'source-layer': 'building',
'filter': ['==', 'extrude', 'true'],
'type': 'fill-extrusion',
'minzoom': 15,
'paint': {
'fill-extrusion-color': '#aaa',
// use an 'interpolate' expression to add a smooth transition effect to the
// buildings as the user zooms in
'fill-extrusion-height': [
"interpolate", ["linear"], ["zoom"],
15, 0,
15.05, ["get", "height"]
],
'fill-extrusion-base': [
"interpolate", ["linear"], ["zoom"],
10, 0,
15.05, ["get", "min_height"]
],
'fill-extrusion-opacity': .6
}
}, labelLayerId);
});
</script>
</body>
</html>
I get the following error:
Error: Source layer "my_data" does not exist on source "my_data" as specified by style layer "population"
at i._validateLayer (style.js:274)
at i.addLayer (style.js:576)
at o.addLayer (map.js:1175)
at o.<anonymous> (index3.html:52)
at o.L.fire (evented.js:115)
at o._render (map.js:1619)
at map.js:1683
Can anyone point me in the direction of a possible mistake here and hopefully how to fix it. You can use the example geojson I gave you to try out this example. Just copy paste it into a file called: GeoObs.json if you want the code to right away give the same error.
As the error states, your GeoJSON source does not have a source layer. So you can remove the 'source-layer' property from the map.addLayer call.
Your GeoJSON also needs to be modified to a proper FeatureCollection:
{
"type": "FeatureCollection",
"features": [
{
"geometry": {
"type": "Point",
"coordinates": [
-73.935242,
40.730610
]
},
"properties": {
"X": "1",
"group": "1"
},
"type": "Feature"
}
]
}
For the population layer, comment out the line, because you do not have such a layer:
'source-layer': 'my_data',
And maybe you have an extra ".json" in the URL:
GeoObs.json.json
[ https://jsfiddle.net/c5nauwgx/ ]
This example from MapLibre helped me and also works with Mapbox GL JS. It adds a little interactivity to your map by giving you the option to select a local geojson file within the map.
https://maplibre.org/maplibre-gl-js-docs/example/local-geojson/
Related
I'm using MapBox GL JS v1.4.1
Based on the example here: https://docs.mapbox.com/mapbox-gl-js/example/cluster/
I cannot get my cluster count to display.
I have tried replicating the MapBox example directly and also using my own data but whatever I try results in the count not displaying.
This is what I have:
<div id="map"></div>
mapboxgl.accessToken = 'ACCESS_TOKEN';
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/dark-v10',
zoom: 1
});
My geoJson data:
var geoData = {
"type": 'FeatureCollection',
"features": [
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [151.12100, -33.78420]
},
"properties": {
"title" : "title",
"description": "description"
}
},
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [151.12100, -33.78420]
},
"properties": {
"title" : "title",
"description": "description"
}
}
]
};
Loading the map, adding geoJSON, clusters etc:
map.on('load', function() {
map.addSource("testMapData", {
type: "geojson",
data: geoData,
cluster: true,
clusterMaxZoom: 14,
clusterRadius: 50
});
map.addLayer({
id: "cluster-count",
type: "symbol",
source: "testMapData",
filter: ["has", "point_count"],
layout: {
"text-field": "{point_count_abbreviated}",
"text-font": ["Arial Unicode MS Bold"],
"text-size": 12,
"text-allow-overlap" : true
}
});
map.addLayer({
id: "clusters",
type: "circle",
source: "testMapData",
filter: ["has", "point_count"],
paint: {
"circle-color": "#f1f075",
"circle-radius": 40
}
});
map.addLayer({
id: "unclustered-point",
type: "circle",
source: "testMapData",
filter: ["!", ["has", "point_count"]],
paint: {
"circle-color": "#51bbd6",
"circle-radius": 8,
"circle-stroke-width": 1,
"circle-stroke-color": "#fff"
}
});
});
Based on the above I should get the cluster count on each of my clusters, but I only see the cluster with no count.
The console also shows no errors.
I can't determine if there's an issue with my geoJSON (it validates via the linter here: http://geojsonlint.com/)... or if the issue lies in how I have added the cluster-count layer... or somewhere else entirely.
Currently you are adding the cluster-count layer before the clusters layer so the latter is covering up the former. If you switch the order you will see both: https:///codepen.io/pj_leonard/pen/bGGgYwv?editors=1000
Update your code to the following:
map.on('load', function() {
map.addSource("testMapData", {
type: "geojson",
data: geoData,
cluster: true,
clusterMaxZoom: 14,
clusterRadius: 50
});
map.addLayer({
id: "clusters",
type: "circle",
source: "testMapData",
filter: ["has", "point_count"],
paint: {
"circle-color": "#f1f075",
"circle-radius": 40
}
});
map.addLayer({
id: "cluster-count",
type: "symbol",
source: "testMapData",
filter: ["has", "point_count"],
layout: {
"text-field": "{point_count_abbreviated}",
"text-font": ["Arial Unicode MS Bold"],
"text-size": 12,
"text-allow-overlap" : true
}
});
map.addLayer({
id: "unclustered-point",
type: "circle",
source: "testMapData",
filter: ["!", ["has", "point_count"]],
paint: {
"circle-color": "#51bbd6",
"circle-radius": 8,
"circle-stroke-width": 1,
"circle-stroke-color": "#fff"
}
});
});
Disclaimer: I work at Mapbox
If the order of the layers is correct, pay attention to text font, eg: "text-font": ["MicrosoftYaHeiRegular"]
My javascript map is reading from a json stream that looks like this:
[{"title":"Site1","latitude":18.3764,"longitude":-67.1819},
{"title":"Site2","latitude":18.2001,"longitude":-65.8081},
{"title":"Site3","latitude":18.3878,"longitude":-66.0998}]
The points are being drawn correctly, so that's great.
To this json I'll be adding an inService field. The inService field will be any of the following options: [Monday, Tuesday, Sunday, Thursday] and the json will look like this:
[{"title":"Site1","latitude":18.3764,"longitude":-67.1819,"inService":"Monday"},
{"title":"Site2","latitude":18.2001,"longitude":-65.8081,"inService":"Tuesday"},
{"title":"Site3","latitude":18.3878,"longitude":-66.0998,"inService":"Sunday"},
{"title":"Site4","latitude":18.1246,"longitude":-66.2607,"inService":"Thursday"}]
At the amcharts website, I saw these two new icons: One that blinks and another that's a dot with a circle around it.
So my question is: Using the json above, how can I assign a blinking pin to the different types of values in inService?
For example:
Monday: blinking blue pin
Tuesday: blinking black pin
Sunday: white dot with a circle around it (no blink)
Thursday red dot with a circle around it (no blink)
And finally, we will be adding other types of status. How can I change the shapes? For example, the pins at the top will be squared instead of round?
Thanks for any help.
I tried using SO code preview, but it's not rendering the map. So I've also included it in jsfiddle:
AmCharts.makeChart("mapdiv", {
"type": "map",
"theme": "light",
"imagesSettings": {
"rollOverColor": "#089282",
"rollOverScale": 1,
"selectedScale": 0.5,
"selectedColor": "#089282",
"color": "#13564e",
"selectable": false,
"bringForwardOnHover": false
},
"areasSettings": {
"color": "#D3D3D3",
"autoZoom": true
},
"data": {
"map": "puertoRicoHigh",
"getAreasFromMap": true
},
"dataLoader": {
"url": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/716619/30189.json",
"format": "json",
"showErrors": true,
"postProcess": function(data, config, map) {
// create a new dataProvider
var mapData = map.data;
// init images array
if (mapData.images === undefined)
mapData.images = [];
// create images out of loaded data
for(var i = 0; i < data.length; i++) {
var image = data[i];
image.type = "circle";
mapData.images.push(image);
}
return mapData;
}
}
});
#mapdiv {
width: 100%;
height: 300px;
<script src="https://www.amcharts.com/lib/3/ammap.js"></script>
<script src="https://www.amcharts.com/lib/3/maps/js/puertoRicoHigh.js"></script>
<script src="https://www.amcharts.com/lib/3/plugins/export/export.min.js"></script>
<link rel="stylesheet" href="https://www.amcharts.com/lib/3/plugins/export/export.css" type="text/css" media="all" />
<script src="https://www.amcharts.com/lib/3/themes/light.js"></script>
<script src="https://www.amcharts.com/lib/3/plugins/dataloader/dataloader.js"></script>
<div id="mapdiv"></div>
I want to add 2 or 3 floors for each building using MapBox GL, but when i try using the addlayer, the colours are getting overriden but no layers or floors are added. Been stuck with this for some time now.
I want 3d extrusions of floors in a building.
Tried putting this into a button click as well, but still it is not returning expected results.
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8' />
<title></title>
<meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
<script src='https://api.tiles.mapbox.com/mapbox-gl-js/v0.39.1/mapbox-gl.js'></script>
<link href='https://api.tiles.mapbox.com/mapbox-gl-js/v0.39.1/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.eyJ1IjoiZGFuc3dpY2siLCJhIjoiY2l1dTUzcmgxMDJ0djJ0b2VhY2sxNXBiMyJ9.25Qs4HNEkHubd4_Awbd8Og';
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v9',
center: [-87.61694, 41.86625],
zoom: 15.99,
pitch: 40,
bearing: 20
});
map.on('load', function() {
map.addLayer({
'id': 'room-extrusion10',
'type': 'fill-extrusion',
'source': 'composite',
'source-layer': 'building',
'paint': {
'fill-extrusion-color': 'blue',
'fill-extrusion-height': {
'type': 'identity',
'property': 'height'
},
'fill-extrusion-base': {
'type': 'identity',
'property': 'max_height'
},
'fill-extrusion-opacity': 1
}
});
map.addLayer({
'id': 'room-extrusion11',
'type': 'fill-extrusion',
'source': 'composite',
'source-layer': 'building',
'paint': {
'fill-extrusion-color': 'red',
'fill-extrusion-height': {
'type': 'identity',
'property': 'height'
},
'fill-extrusion-base': {
'type': 'identity',
'property': 'min_height'
},
'fill-extrusion-opacity': 1
}
},'room-extrusion10');
});
</script>
</body>
</html>
fill-extrusion-height and fill-extrusion-base are the paint properties that control the lower and upper height (in meters) of a fill-extrusion feature. In your example these are styled with identity functions based on building height data from OpenStreetMap (in the Mapbox Streets data source), causing them to imitate real-world building shapes (as best as they are mapped). In order to create a fill-extrusion layer that spans only one floor you'd want fill-extrusion-height: 3 (or however tall, in meters, you perceive one story to be) and fill-extrusion-base: 0 for the first floor; the second would be height=6 and base=3, and so forth.
Note that in all released versions of Mapbox GL JS, multiple fill-extrusion layers don't render correctly with respect to each other. This is fixed in master and will be included in the next release this month.
I am trying to use Mapbox/Turfjs to understand how many points are in a polygon. I have rendered my polygon and points in map.onload Is then possible to call Turf.js from another function AFTER the polygon and points have been rendered to the map?
Something like this...?
$(document).ready(function(){
mapboxgl.accessToken = 'eeeeeeeeee';
map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v9',
center: [ 32.62939453125,1.7355743631421197],
zoom: 6.5,
pitch: 40,
maxZoom: 17
});
map.on('load', function () {
//geofence data
map.addSource('fencedata', {
type: 'geojson',
data: 'data/fence.geojson'
});
map.addLayer({
id: 'fence',
type: 'fill',
"source": "fencedata",
'layout': {},
'paint': {
'fill-color': '#FF0000',
'fill-opacity': 0.3
}
});
//points data
map.addSource("pointdata", {
type: "geojson",
data: 'data/points.geojson',
cluster: true,
clusterRadius: 20
});
map.addLayer({
"id": "points",
"type": "circle",
"source": "pointdata",
"paint": {
'circle-color': 'rgba(255, 255, 46, 1.0)',
'circle-radius': 8
}
});
});
map.addControl(new mapboxgl.NavigationControl());
});
geofence();
function geofence(){
var ptsWithin = turf.within(points, fence);
}
You have your points as GeoJSON, and your polygon as GeoJSON - so, yes, you can use TurfJS to find out which points are within the polygon. It looks like the code you've proposed is correct. The fact you're using Mapbox is irrelevant to this particular task.
If you're experiencing a problem with this approach, indicate it in your question.
Try to add the geofence() function inside the map on-load function after adding those layers, By that way you can make sure that the geofence() function is called after the layers have been loaded
map.on('load', function () {
//geofence data
map.addSource('fencedata', {
type: 'geojson',
data: 'data/fence.geojson'
});
map.addLayer({
id: 'fence',
type: 'fill',
"source": "fencedata",
'layout': {},
'paint': {
'fill-color': '#FF0000',
'fill-opacity': 0.3
}
});
//points data
map.addSource("pointdata", {
type: "geojson",
data: 'data/points.geojson',
cluster: true,
clusterRadius: 20
});
map.addLayer({
"id": "points",
"type": "circle",
"source": "pointdata",
"paint": {
'circle-color': 'rgba(255, 255, 46, 1.0)',
'circle-radius': 8
}
});
geofence();
});
map.addControl(new mapboxgl.NavigationControl());
});
function geofence() {
var ptsWithin = turf.within(points, fence);
}
I've countries GeoJson with a geometry of type polygon. Each polygon has only one property name (name is the country name). each polygon represents a country.
Now I want to paint each polygon with different color depending on the value of the name property, but this is not working properly.
See this JS bin Demo
map.on('load', function () {
map.addLayer({
'id': 'maine',
'type': 'fill',
'layout': {},
'paint': {
'fill-color': {
property: 'name',
stops: [
['Albania', '#F2F12D'],
['Algeria', '#7A4900'],
['Australia', '#63FFAC'],
["South Africa", "#4FC601"],
["South Korea", "#3B5DFF"],
]
},
'fill-opacity': 0.8
},
'source': {
'type': 'vector',
'url': 'mapbox://saurabhp.countries_tileset'
},
"source-layer": "countries",
});
});
Every thing in your code was perfect, the only thing which you have missed out is type: 'categorical' inside the fill-color, You need to specify the type
Checkout this link for more details run the below code snippet to see the demo
mapboxgl.accessToken = 'pk.eyJ1Ijoic2F1cmFiaHAiLCJhIjoiY2l6OWRwM2JlMDAxZTJ3b2ZwejAzdzhpdSJ9.nc4cEdRwhErEg2wuUkABbw';
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v9',
center: [-1.41, 6.32],
zoom: 5
});
map.on('load', function () {
map.addLayer({
'id': 'maine',
'type': 'fill',
'layout': {},
'paint': {
'fill-color': {
property: 'name',
type: 'categorical',
stops: [
['Albania', '#F2F12D'],
['Algeria', '#7A4900'],
['Australia', '#63FFAC'],
["South Africa", "#4FC601"],
["South Korea", "#3B5DFF"],
]
},
'fill-opacity': 0.8
},
'source': {
'type': 'vector',
'url': 'mapbox://saurabhp.countries_tileset'
},
"source-layer": "countries",
});
});
<html>
<head>
<meta charset='utf-8' />
<title></title>
<meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
<script src='https://api.tiles.mapbox.com/mapbox-gl-js/v0.37.0/mapbox-gl.js'></script>
<link href='https://api.tiles.mapbox.com/mapbox-gl-js/v0.37.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>
</body>
</html>