Mapbox GL JS: How can I use JavaScript with expressions to change layer layout? - geojson

I'm using Overpass to fetch OSM data and in Mapbox show the closest point of interest of a couple of 'categories'. I convert the OSM data to a GeoJSON layer and add it to Mapbox as a symbol layer with a marker image on each point in the data. The result looks like this:
I want to change the text displayed beneath each marker to something other than the variables I use to categorize the data. My first idea was to use a JS object like:
translate = {
'water': 'Waterway',
'city': 'City Center',
'school': 'Elementary School',
..etc
}
and then run each name through translate[venue_type]. This doesn't seem to work that easily though.
The marker is added as a layer like this:
map.loadImage(
'./img/map_marker3.png',
(error, image) => {
if (error) throw error;
if (!map.hasImage('custom-marker')) {
map.addImage('custom-marker', image);
}
map.addSource('venues',
{
'type': 'geojson',
'data': data
});
map.addLayer({
"id": "venues",
"type": "symbol",
"source": "venues",
'layout': {
'visibility': 'none',
'icon-image': 'custom-marker',
'icon-size': 1,
'icon-anchor': 'bottom',
'icon-allow-overlap': true,
// get the venue_type from the feature's venue_type property
'text-field': ['get','venue_type'],
'text-allow-overlap': true,
'text-font': [
'Open Sans Semibold',
'Arial Unicode MS Bold'
],
'text-offset': [0, 0.5],
'text-anchor': 'top'
}
});
callback();
});
with a GeoJson FeatureCollection of the following structure:
"type": "Feature",
"properties": {
"dist": 0.25566043226822727,
"index": 758,
"location": 21.218387011937942,
"name": "Östersjön",
"venue_type": "coastline"
},
"geometry": {
"type": "Point",
"coordinates": [
18.3434769,
59.4563257
]
}
The line responsible for setting the text underneath the marker is the following, which uses a Mapbox GL JS expression to fetch the field 'venue_type' from the properties of the GeoJSON object.
'text-field': ['get','venue_type']
I figured I could just do something like
'text-field': translate[['get','venue_type']]
but it gives the error:
Error: layers.venues.layout.text-field: 'undefined' value invalid. Use null instead.
at Object.ri [as emitValidationErrors]
text-field can take both a string, variable and a mapbox expression. But I'm not really sure if I can access the properties of the feature being added in any other way than the expression above.
Having looked into modifying the expression, it appears to be possible to use arithmetics and stuff, but I can't really find any example of mixing JavaScript into the expression.
Looking for some pointers here, should I go about it a different way? Or am I missing something?

First, this is a symbol layer, not a Marker.
You can't execute arbitrary JavaScript expressions within Mapbox GL expressions.
You can use a match expression, like this:
'text-field': [`match', ['get', 'venue_type'],
'water', 'Waterway',
'city', 'City Center',
'school', 'Elementary School',
''
]

Related

How to cross reference a property in JMESPath or JSONPath?

I am trying to use jmespath for my querying my json and I am trying to access a property which is at a higher level.
Below is my JSON:
{
"properties": {
"DefaultVMTypeID": "RT",
"VM": {
"measurements": [
{
"vm": 45.62,
"vmString": "45.62",
"vmID": "RT",
"vmPathID": "osdu",
"vmTypeID": "RT",
"vmUnitOfMeasureID": "m"
},
{
"vm": 65,
"vmString": "65",
"vmID": "MT",
"vmPathID": "sample",
"vmTypeID": "MT",
"vmUnitOfMeasureID": "m"
},
{
"vm": 32,
"vmString": "32",
"vmID": "MT",
"vmPathID": "osduschemas",
"vmTypeID": "MT",
"vmUnitOfMeasureID": "m"
},
{
"vm": 95,
"vmString": "95",
"vmID": "MT",
"vmPathID": "schema",
"vmTypeID": "MT"
}
]
}
}
}
I want to get all the measurements whose vmId is equal to DefaultVMTypeID.
I tried below query:
[properties.DefaultVerticalmeasurementTypeID, properties.Verticalmeasurements.measurements[?VerticalmeasurementTypeID]] | map(&[1].VerticalmeasurementTypeID==#[0], #)
but when applying map to the array # refers to the element of the array and inside map there is no way I an access DefaultVMTypeID.
I have also tried transforming each element of an object.
Any leads would be appreciated.
I came across the TS translation on JMESPath library which has 2 enhancements.
Custom functions
Root Value Access
https://github.com/nanoporetech/jmespath-ts
This helped me solve my problem.
[properties.Verticalmeasurements.measurements][] | [?VerticalmeasurementID==$.properties.DefaultVerticalmeasurementTypeID]
You can actually use $ inside expressions to access the root of the JSON.
So the JSON Path
$.properties.VM.measurements[?(#.vmID==$.properties.DefaultVMTypeID)]
should work to get all the items in the measurements array that match.

Return values for dimensions in timeseries query Druid

I have Druid timeseries query:
{
"queryType": "timeseries",
"dataSource": {
"type": "union",
"dataSources": [
"ds1",
"ds2"
]
},
"dimensions":["dim1"],
"aggregations": [
{
"name": "y1",
"type": "doubleMax",
"fieldName": "value1"
}
],
"granularity": {
"period": "PT10S",
"type": "period"
},
"postAggregations": [],
"intervals": "2017-06-09T13:05:46.000Z/2017-06-09T13:06:46.000Z"
}
And i want to return the values of the dimensions as well, not just for the aggregations like this:
{
"timestamp": "2017-06-09T13:05:40.000Z",
"result": {
"y1": 28.724306106567383
}
},
{
"timestamp": "2017-06-09T13:05:50.000Z",
"result": {
"y1": 28.724306106567383
}
},
How do I have to change the query? Thanks in advance!
If your requirement is to use dimension column in time series query that means you are using aggregated data with non aggregated column, this requirement leads to the use of topN or groupBy query.
groupBy query is probably one of the most powerful druid currently supports but it has poor performance as well, instead you can use topN query for your purpose.
Link for topN documentation and example can be found here:
http://druid.io/docs/latest/querying/topnquery.html
Is Timeseries() query is not supporting dimension?
I tried it in my project but it is not working.
here is Error:
TypeError: queryRep.dataSource(...).dimension is not a function
2|DSP-api | at dimensionData (/home/ec2-user/reports/dsp_reports/controllers/ReportController.js:228:22)
Let me know if anyone has a solution for this.
TY.

Siri shortcuts rounding number in "Get Contents of URL" POST

I am trying to create a iOS 12 Shortcut based on the Gautrain API.
I want to do a POST to the URL https://api.gautrain.co.za/transport-api/api/0/journey/create with the following payload:
{
"geometry": {
"coordinates": [
[
28.23794,
-25.74762
],
[
28.05693,
-26.10858
]
],
"type": "MultiPoint"
},
"profile": "ClosestToTime",
"maxItineraries": 3,
"timeType": "DepartAfter",
"only": {
"agencies": [
"edObkk6o-0WN3tNZBLqKPg"
]
}
}
I have entered all these details into a "Get Contents of URL" block. For the elements of the "coordinates" arrays I have used "Number".
The problem is that when I track what my phone is sending via mitmproxy, it sends all the information correctly, but the coordinates have been rounded to integers:
{
"geometry": {
"coordinates": [
[
28,
-25
],
[
28,
-26
]
],
"type": "MultiPoint"
},
"maxItineraries": 1,
"only": {
"agencies": [
"edObkk6o-0WN3tNZBLqKPg"
]
},
"profile": "ClosestToTime",
"timeType": "DepartAfter"
}
For this reason, the request is not giving the desired results.
I have a feeling this may be a bug, but is there something I am missing where I can tell Shortcuts to use the full set of digits?
I have found the problem. Since I am in South Africa, the numbers are expected to have commas instead of periods for decimals. I would have loved some feedback in the field that this wasn't a valid number instead of just silently ignoring the decimal.
The solution therefore was to change the "28.23794" in the entry box to "28,23794".
I might also link to postman-echo.com as an excellent tool for debugging these kinds of requests.

JSON-LD normalization - ignore JSON nesting

I'm working on JSON-LD serialization, and ideally I would like to have a #context which I can add to the existing GeoJSON output (together with some #ids and #types), so that both the Turtle output and the JSON-LD output will normalize to the same triples.
Data is organized as follows: each object/feature has an ID and a name, and data on one or more layers. Per layer, there is a data field, which contains a JSON object.
Example GeoJSON output:
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {
"id": "admr.nl.appingedam",
"name": "Appingedam",
"layers": {
"cbs": {
"data": {
"name": "Appingedam",
"population": 1092
}
},
"admr": {
"data": {
"name": "Appingedam",
"gme_code": 4654,
"admn_level": 3
}
}
}
},
"geometry": {…}
}
]
}
Example Turtle output:
<admr.nl.appingedam>
a :Node ;
dc:title "Appingedam" ;
:createdOnLayer <layer/admr> ;
:layerData <admr.nl.appingedam/admr> ;
:layerData <admr.nl.appingedam/cbs> .
<admr.nl.appingedam/admr>
a :LayerData ;
:definedOnLayer <layer/admr> ;
<layer/admr/name> "Appingedam" ;
<layer/admr/gme_code> "4654" .
<layer/admr/admn_level> "3" .
<admr.nl.appingedam/cbs>
a :LayerData ;
:definedOnLayer <layer/cbs> ;
<layer/cbs/name> "Appingedam" ;
<layer/cbs/population> "1092" ;
The properties object does not have its own URI. Is there a way to create a JSON-LD context which takes the contents of the properties into account, but further 'ignores' its precence?
Answered by Gregg Kellogg on JSON-LD mailing list:
This is something that keeps coming up: having a transparent layer,
that basically folds properties up a level. This was discussed during
the development of JSON-LD, but ultimately it was rejected.
I don't see any prospects for doing something in the short-term, but
it could be revisited in a possible future WG chartered with revising
the spec. Feedback like this is quite useful.
In the mean time, you can play with different JSON-LD encodings that
match your RDF though tools like http://json-ld.org/playground and my
own http://rdf.greggkellogg.net/distiller.
Gregg

dust js: aliasing an object not working

Template
{#person alias=root}{alias.value}: {name}, {age}{/person}
data:
{
"root": {value:"MR."},
"person": {
"name": "Larry",
"age": 45
}
}
Expected output:
MR. Larry, 45
Actual output:
: Larry, 45
I'm trying to alias an object like shown above. But its not working. Please have a look into this fiddle http://jsfiddle.net/G86mu/1/.
If i replace {value:"MR."} with a string say "root":"Mr." and change my template to
{#person alias=root}{alias}: {name}, {age}{/person}
the output is as expected. Please let me know how do i alias an object
The reason this isn't working is because the context within Dust is not the same as the JSON you pass in to dust.render. Internally, Dust wraps your JSON so that it can include params, globals, and blocks in the context.
So, you are not adding alias to the current context, as you might assume. Instead, you are adding alias one level above your current context. Although the representation isn't exactly accurate, it should be helpful for explanation purposes:
// Incorrect:
{
"root": {
"value": "MR."
},
"person": {
// Current context
"alias": {
"value": "MR."
},
"name": "Larry",
"age": "45"
}
}
// (more) correct:
{
"root": {
"value": "MR."
},
"alias": {
"value": "MR."
"person": {
// Current context
"name": "Larry",
"age": "45"
}
}
}
When the context is viewed in this way, it makes sense why {#person alias=root}{alias.value}: {name}, {age}{/person} will not work. When using the dot-notation inside of a reference (as in {alias.value}, dust starts in the current context and goes down. Since there is no "alias" object inside of the current context, dust gives up, and you get an empty string.
However, if when you don't use the dot-notation, dust starts at the current context and searches up. The first time it finds a match, it will use that match. So, for your example, you could use the following to get your expected output.
{#person alias=root}{#alias}{value}{/alias}: {name}, {age}{/person}
Alternatively, if you could use:
{#person aliasVal=root.value}{aliasVal}: {name}, {age}{/person}

Resources