Using a custom Mapbox Style's internal GeoJSON data as a source - mapbox

I'm currently trying to render a map, with data coming from shapefiles (transformed into GeoJSON), upon uploading to Mapbox's servers.
My demo output can be viewed at:
https://ciatph.github.io/amia-lowres-hover.html
I would like to inquire if I have rendered the map efficiently, as used in the demo page. So far, I have:
Uploaded a GeoJSON Dataset
Exported the Dataset into a Tileset
Added the Tileset into a Style
Used the Style to load an initial base map
Used the uploaded Dataset as a data source for another Layer (on top of the initial map). This Layer listens and responds to mouse hover and click events
Used the uploaded Dataset as a data source for another Layer with Filters to color the hovered region differently for the created Layer in step #5.
Screenshot of relevant Mapbox script
My questions for this approach are:
Is there a way to use the Style's internal (GeoJSON) Dataset for the map.addSource() part, such that it doesn't need to be re-loaded or redefined again for creating interactive Layers? I'm concerned of network activity if its being re-downloaded again at this point. I'm also interested to know if this is possible, because we have large GeoJSON data that are almost 100MB in size. This gets automatically converted into Tileset upon moving from Amazon S3 temporary servers to mapbox, and there are no Dataset created to play with the map.addSource() part
I can use Mapbox's default styles for basemap, (i.e., mapbox://styles/mapbox/streets-v9), and omit step #4. If I go for this approach, will the Dataset loading (for step #5) be efficient and fast enough for large data, as opposed if its used or loaded via Style?
I hope you can help me with my queries and enlighten me of more salable and efficient approaches. Thank you.

Let's start by clarifying your current situation:
Your style contains a vector tileset with id ciatph.cj64in9zo1ksx32mr7ke3g7vb-93srz, referred to in your style as amia-lowres-tileset within the composite vector tile source.
It is also accessible as a dataset through the ID ciatph/cj64in9zo1ksx32mr7ke3g7vb, since you uploaded it as a dataset.
Your script is loading the dataset at runtime.
I don't see any reason you need to refer to the dataset instead of the tileset. So, remove the code that adds the dataset, and update the two styles to refer to the tileset (source: "composite") instead.
// Only used for coloring hover effect. Data informatiion be retrieved from styles alone
/*
map.addSource("dataSource", {
"type": "geojson",
'data': 'https://api.mapbox.com/datasets/v1/ciatph/cj64in9zo1ksx32mr7ke3g7vb/features?access_token=pk.eyJ1IjoiY2lhdHBoIiwiYSI6ImNqNXcyeTNhcTA5MzEycHFpdG90enFxMG8ifQ.gwZ6uo6pvx4-RZ1lHODcBQ'
});
*/
// add layer to color the raw source data
map.addLayer({
'id': 'municipalities',
"type": "fill",
"source": "composite",
"source-layer": "amia-lowres-tileset",
"layout": {},
"paint": {
"fill-color": "#627BC1",
"fill-opacity": 0.5
}
});
// add a conditional layer to play over the source data on hover event
map.addLayer({
"id": "state-fills-hover",
"type": "fill",
"source": "composite",
"source-layer": "amia-lowres-tileset",
"layout": {},
"paint": {
"fill-color": "#ff4",
"fill-opacity": 1
},
"filter": ["==", "MUNI_CITY", ""]
});
https://codepen.io/stevebennett/pen/OjvMWO

Related

What I need to put "url" in mapbox addSource

This is my first time using mapbox and I can't figure out how to addSource when map load.
Below is the sample code.
I uploaded 'KML' file for tilesets and I want to use this tileset for source, but I don't know how to write 'url' part.
I also want to know what is 'source-layer'. What should I write in the 'source-layer?
I am sorry I know this is very basic question, but I really need to know.
Please help me.
Thanks.
map.on('load', function() {
// Add the source to query. In this example we're using
// county polygons uploaded as vector tiles
map.addSource('counties', {
"type": "vector",
"url": "mapbox://mapbox.82pkq93d" <<---here
});
map.addLayer({
"id": "counties",
"type": "fill",
"source": "counties",
"source-layer": "original", <<---source layer
"paint": {
"fill-outline-color": "rgba(0,0,0,0.1)",
"fill-color": "rgba(0,0,0,0.1)"
}
}, 'place-city-sm'); // Place polygon under these labels.
});
EDIT:
Your tile url should also be fine like this:
mapbox://{}
It is a bit tricky to find in the documentation: When uploading KML you are creating tileset for which you should get a map ID. With the map ID you can either request separate tiles using a tile url like this:
/v4/{map_id}/{zoom}/{x}/{y}{#2x}.{format}
You can use the tile url when adding a source
map.addSource({
type: 'vector',
tiles: ['https://api.mapbox.com/v4/{map_id}/{zoom}/{x}/{y}.mvt']
});
Or you can request a TileJSON metadata object and use this to add the source:
map.addSource({
type: 'vector',
url: 'https://api.mapbox.com/v4/{map_id}.json' // <-- tileJSON url
});
For your source layer question: Vector tiles include multiple "layers" of data/geometry, when adding a map layer you need to define which source-layer the map layer refers to. E.g. you can have a single vector tile set consisting of line strings and points (two different source-layers), but your map layer should only render on of them. You can either check to tile JSON to see what source-layers are included in your tile set or create a map style in mapbox studio, using your uploaded tile set as a source.

Mapbox - Update data-driven styling in session

There are several tutorials on data-driven styling on Mapbox's site, but none of them address how to update an existing data-driven style (created in Mapbox Studio) in-session. For example, say I want to create a choropleth of US states, colored by area. User then selects 'color by population' and the color of the states update accordingly.
I've found some resources that would allow one to achieve this by adding the geojson of all the states and then doing map.addLayer, however the geojson I'm working with (census tracts) is too massive to add to the front-end, so I needed to change the data-driven styling of an existing layer (or find a similar work-around). The data has to be a persisted tileset from mapbox studio, with user updating the data coloration based on geojson properties.
Any ideas or examples would be much appreciated.
Updating data-driven styling at run-time is straightforward. You simply call map.setPaintProperty.
I usually implement this with one function that generates the property value. Something like this:
function fillColorByPopulation(min, max) {
return {
property: 'pop',
stops: [
[min, 'red'],
[max, 'blue']
],
type: 'exponential'
}
}
function updateStyle(prop) {
if (prop === 'population') {
map.setPaintStyle('regions', 'fill-color', fillColorByPopulation(data.minpopulation, data.maxpopulation));
} else {
map.setPaintStyle('regions', 'fill-color', 'transparent');
}
}
I generally don't create any data-driven styles within Mapbox Studio. It's simpler to create them all in Javascript.

How to use a remote wmts and generate its tiles in mapbox

according to How to implement a tile source to mapbox-gl i have an issue on a raster tile source in mapbox gl JS, which probably seems to fit to your declaration that mapbox only supports x/y/z and not lat/lon tile coordinates. I've trouble on this with the following tile source (WMTS): https://www.wmts.nrw.de/geobasis/wmts_nw_dop20/1.0.0/WMTSCapabilities.xml.
I want to include this wmts as source and add as layer like this:
map.on("load", function() {
map.addSource("wmts-layer", {
"type": "raster",
"tiles":['https://www.wmts.nrw.de/geobasis/wmts_nw_dop20/tiles/nw_dop20/EPSG_3857_16/{z}/{x}/{y}.jpeg'],
"tileSize": 256
});
map.addLayer({
"id": "wmts-layer",
"source": "wmts-layer",
"type": "raster",
"visibility": "visible",
"source-layer": "nw_dop20",
});
});
it does not work at all, Tiles get loaded but are empty images!
Can anyone point out whats the problem here?
cheers phil
You seem to be using Mapbox-GL-JS correctly (although you don't need source-layer). For whatever reason, that service is returinng blank tiles for example.
Thank you for investigation Steve,
this is really weird and for some reason the service seems to be unusable in mapbox gl as it serves different coordinates on each zoomlevel than it is specified in the tilescheme of mapbox (tms or xyz)!
see:
https://github.com/mapbox/mapbox-gl-js/issues/6089

fancytree folders never collapse

fancytree folders are never collapsed. All children and parents are displayed without correct nesting structure.
When I copy the exact same data that works in text data source, instead from a web2py (python) controller the folders will not collapse but just display permanently expanded. No js console errors in browser.
original data that works perfectly in text file
FancyTree copies data from python contoller like this
json_list = [{
"alexLink": "http://example.com/",
"kind": "tasks#task",
"id": "MTYwNzEzNjc2OTEyMDI1MzcwNzM6ODUwNjk4NTgzOjExMTkyODk2MjA",
"etag": "\"4qyCALf1j510T_-I20NAMbUHF2k/LTEzNTgzMTMzODg\"",
"title": "Task 01",
"updated": "2015-04-23T19:25:44.000Z",
"selfLink": "",
"position": "00000000002147483647",
"status": "needsAction"
}]
I convert to json: json_list = json.dumps(json_list)
Then use as source:
// Initialize Fancytree
$("#alexTree").fancytree({
checkbox: true,
selectMode: 3,
source: {{=XML(json_list)}},
postProcess: function(event, data){
data.result = convertData(data.response);
},
select: function(event, data) {
window.open(data.node.data.alexLink, "_blank");
Data looks same as in text file source. What could be causing the folders to not contract with children under them?
I can't see an obvious reason, why your sample is not working (I assume persistence extension is off?).
Except maybe for {{=XML(json_list)}}, that may do something unexpected.
I guess a debuggable demo is needed, to find out.
Anyway, your sample does this:
Generate tree data in a native (none-Fancytree) format
Generate a html page with an embedded <script> tag that in turn has this data embedded as string
On page-load the tree is created with native data and post processing is done client-side
Fancytree was designed with this pattern in mind (among others):
Have a static page with an empty <div id="tree"> element and include some JavaScript
On page-load initialize the tree and pass an ajax url for source.
This will immediately display the page for your users, while loading is deferred (showing a spinning icon)
A soon as the data arrives, the tree will update.
(You could also consider to do the conversion server-side and send valid Fancytree-compatible data.)
This would probably deliver a smoother user experience. It would also allow to refresh tree data without reloading the page, by calling tree.reload().

Leaflet Layer Control: Ordering

We use the standard "layer control" from Leaflet. Instantiation is as follows:
this.controls[id] = L.control.layers({}, {}, {
collapsed: false
});
Layers are added dynamically and in random order (depending on when xhr requests complete). However it seems that leaflet implicity uses the _leaflet_id of the respective layer for ordering in the layer control and thus our layers appear in random order.
Is there any way to tell leaflet in which order it should display the layers in the control? E.g. by passing some additional parameter when calling the .addOverlay or .addBaseLayer methods?
Leaflet version is 1.0-dev
I found some hack-fix for the problem. However this is not a clean solution so feel free to post better answers.
Leaflet uses the _leaflet_id of the layer object for sorting. Thus the layer created first will always be on top. In order to sort the layers you will have to wrap the actual layer in a layer/feature group.
|- dummy layer group
|- some dummy geojson layer
|- your actual layer (will be added later)
Programatically:
var oFeatureGroup = L.featureGroup([
L.geoJson({
"type": "FeatureCollection",
"features": []
})
]);
Then we do some async loading and once we are ready we add the layer to the - more or less empty - layer group:
oFeatureGroup.addLayer(oMyMagicAsyncLoadedLayer)
We can then add the layer to the layer control and it will be sorted as "expected" where the order depends on the order of creation of the oFeatureGroup layers instead of the oMyMagicAsyncLoadedLayer.