mapbox / mapbox-gl-js

Interactive, thoroughly customizable maps in the browser, powered by vector tiles and WebGL
https://docs.mapbox.com/mapbox-gl-js/
Other
11.15k stars 2.21k forks source link

Support nested objects and arrays for GeoJSON features in query*Features #2434

Open hsinister opened 8 years ago

hsinister commented 8 years ago

v0.16.0:

Hi! This is my first issue report, so I hope I get it right.

Basically I'm trying to use the function queryRenderedFeatures to get the underlying data in JSON format to present on a popup. This works for the higher level of the JSON data (I can show the data on the popup), but fails to get the more deeply nested data - a console.log suggests that the objects have been converted into a string instead.

Here's the link to the issue as demonstrated using the example on "popup on hover": jsbin

I've edited the data such that there is only a single marker and also added an array of objects called "list" under "properties" a la feature.properties.list. I've also changed the popup HTML to display the first element of what is supposed to be a list.

Expected Behavior

I expect that on hover the popup should display the first object, {"id":1,"name":"A"}.

Actual Behavior

Instead, it shows "[", which confirms that it returns a string instead of the object.

tmcw commented 8 years ago

Looks like a valid bug to me! The queryRenderedFeatures results are definitely coming out JSON-encoded and entering as objects.

lucaswoj commented 8 years ago

@mourner @ansis Do we support arrays and objects within feature properties? If not, should we support arrays and objects within feature properties?

ansis commented 8 years ago

Do we support arrays and objects within feature properties?

Not right now. Why:

The vector tile spec doesn't support array or object values. After geojson is tiled, it gets converted to a vector tile pbf so that it can be transferred from the worker to the main thread efficiently for querying. Converting it to a vector tile might not be the best idea, but that's why it's happening.

If not, should we support arrays and objects within feature properties?

I think so. I'm not 100% sure, but I think so

Related questions:

mcwhittemore commented 8 years ago

should queries return the full geojson instead of the tiled geojson?

Is this even possible? To get the full geojson all the tiles of the feature would have to be loaded. We could do this for geojson sources, but its more important that the api is consistent across source types than reporting full geojson.

This API being sync is also a very crucial thing so that it can play with mouse events nicely.

That said, what a user expects is to have the feature that is being rendered be returned and while we can't return the geometry, we can return the properties. Returning these how the user originally represented it would be good for clarity.

mourner commented 8 years ago

This is a limitation of the vector-tile-spec — there's no "JSON" value.

We could add an extension (a new field type that would encode stringified JSON including arrays), but it's a hack and we would have to add support for this upsteam in vt-pbf. A safer solution might be to write a custom pbf encoder of tiled GeoJSON and have that code in the repo, not exposed anywhere else — after all, encoding/decoding for transfer between threads is an implementation detail.

shkfnly commented 8 years ago

Has there been any further resolution to this issue?

gmaclennan commented 8 years ago

Just got hit by this, adding a GeoJSON layer with features like:

{
  "properties": {
    "picture": {
      "url": "https://example.com/image.jpg"
    },
  },
  "geometry": {
    "coordinates": [
      -59.98021487775259,
      2.8771639400161804
    ],
    "type": "Point"
  },
  "type": "Feature",
  "id": "7b8f0c7d-0d08-4029-ab66-eca3517e8aea"
}

What I get from map.queryRenderedFeatures is:

{
  "properties": {
    "picture": "[object Object]",
  },
  "geometry": {
    "coordinates": [
      -59.98021487775259,
      2.8771639400161804
    ],
    "type": "Point"
  },
  "type": "Feature",
  "id": "7b8f0c7d-0d08-4029-ab66-eca3517e8aea"
}

This needs to be documented as a breaking change in CHANGELOG.md from when featuresAt was removed.

The API docs also need updating, they currently incorrectly state:

Returns Array: features - An array of GeoJSON features matching the query parameters. The GeoJSON properties of each feature are taken from the original source.

shkfnly commented 8 years ago

@gmaclennan Did map.featuresAt return the whole nested geojson previously?

gmaclennan commented 8 years ago

Yes, in v0.15. Just upgraded today to 0.18 and seeing this change.

gmaclennan commented 8 years ago

On a related note, queryRenderedFeatures also does not not return the id property on a feature. i.e. not properties.id but the top-level id prop as defined in the GeoJSON spec. I am guessing this is probably a geojson-vt issue, which at a quick glance at the code does not seem copy that?

mourner commented 8 years ago

@gmaclennan yep, looks like it. I'd gladly merge a PR for this. :)

gmaclennan commented 8 years ago
anandthakker commented 8 years ago

Note: I believe https://github.com/mapbox/vt-pbf/pull/6 (published in vt-pbf 2.1.0) should change the behavior that @gmaclennan described: non-primitive properties will now get serialized as JSON (but note that they won't get automatically _de_serialized, as there's no mechanism for recording the encoding)

zlavergne commented 7 years ago

What is the status of this feature? Is there any support for nested arrays?

marr commented 7 years ago

bump

waissbluth commented 6 years ago

Adding support for this would be especially useful now that expressions are supported per #4777. Is there any way of working with array properties yet, or plans for it?

anandthakker commented 6 years ago

@waissbluth you should be able to use structured feature property data (arrays/objects) in expressions for layers using geojson sources. (However, per this issue, if you use queryRenderedFeatures, you'll see that those arrays/objects show up serialized as strings at that point.)

For vector tile support, see https://github.com/mapbox/vector-tile-spec/issues/75

mrshll commented 6 years ago

@anandthakker I'm having trouble getting this working with even geojson layers.

{
  'fill-color': {
    property: 'user.num',
    type: 'categorical',
    stops: [...],
  }
}

where the features have some property like {user: {num: 1}} doesn't seem to style a fill layer. I'm having similar trouble on text-field on a symbol layer. Do I have to flatten all data-driving properties to property's top level, even for geojson layers?

lukemiller commented 6 years ago

I have the same question as @mmoutenot. Looking to apply data driving styling from a property object.

line_color = { base: 1, property: 'analysis.data.volume', stops: [ ... ], }

all data is geojson

anandthakker commented 6 years ago

The old stops-based approach to data-driven styling doesn't (and won't) support nested properties; instead, you'll need to use expressions -- specifically, you can use the "get" expression like so: ["get", "volume", ["get", "data", ["get", "analysis"]]]. To replicate stop-function-like behavior using expressions, use "interpolate", e.g.:

[
  "interpolate",
  ["linear"],
  ["get", "volume", ["get", "data", ["get", "analysis"]]],
  0,
  "green",
  10,
  "blue"
]
ujazdowskip commented 6 years ago

@anandthakker I'am trying to write an expression to get data from nested property to use as a text-field in symbol layer.

"properties": {
  "a": {
    "b": "some value"
  }
}
[
  "get", "b",
  ["get", "a"]
]

I get the following error:

[2]: Expected object but found value instead.

The docs clearly says that:

["get", string, object]: value

So get is expecting object as a second parameter and returns value type. So my questions are:

  1. Is value an object?
  2. Is this expected behavior a bug?

It would be really nice if such query for nested property was possible.

anandthakker commented 6 years ago

Is value an object?

value is a type that means, essentially, "some JSON value" -- i.e., it could be null, a number, string, boolean, array, or object. In most cases, when a value-typed expression is provided but we need a more specific type, we implicitly "type assertion". This is one case where we don't do so, but we should (tracking at #6235). Meanwhile, if you change your expression to [ "get", "b", ["object", ["get", "a"]]], it should work.

janbaykara commented 6 years ago

@anandthakker there is no object type mentioned in the docs, and I've not been able to use that successfully.

anandthakker commented 6 years ago

@janbaykara apologies, the "object" assertion does exist, but is missing from the docs.

I've not been able to use that successfully.

Could you please provide a minimal working example that reproduces this?

janbaykara commented 6 years ago

@anandthakker ignore me, I'm a moron. I was searching (a <- b) rather than (b <- a)

(i.e. [ "get", "a", ["object", ["get", "b"]]] not [ "get", "b", ["object", ["get", "a"]]])

anandthakker commented 6 years ago

👍 glad it's working!

loganpowell commented 6 years ago

Can someone point me to the area in the docs that explains how to use nested objects within the properties of the GeoJSON?

loganpowell commented 6 years ago

Sorry, missed the reference to expressions https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions

rebz commented 5 years ago

I see people discussing styling data using nested Objects within a feature's properties. I opened an issue, #7620 and was referred here. Are expressions with nested objects just "sort-of" implemented?

mourner commented 5 years ago

@rebz it stops working when you use feature state: see also #7194.

rebz commented 5 years ago

@rebz it stops working when you use feature state: see also #7194.

@mourner Thanks for pointing that out, exactly what I'm experiencing. Will just need to re-think how I format my Geojson data for styling. Appreciate the quick response time.

fc commented 5 years ago

@waissbluth you should be able to use structured feature property data (arrays/objects) in expressions for layers using geojson sources. (However, per this issue, if you use queryRenderedFeatures, you'll see that those arrays/objects show up serialized as strings at that point.)

For vector tile support, see mapbox/vector-tile-spec#75

This comment applies for how to convert a vector tileset to a geojson source and run an expression on it.

If anyone is wondering on a possible way to do this using one of the composite/vector layers then you'll need to make a copy of the vector layer then add it is a geojson source. Update the data by doing a JSON.parse on the property you want as JSON.

In our case we were unable to query an array of objects since it was being converted to a JSON string on the property.

This is definitely hacky but maybe an option for someone who doesn't have control of the data going into Mapbox.


const features = this._map.querySourceFeatures('composite', {
    sourceLayer: 'your_tileset_name'
});
this.addSource('some-source-name', {
    type: 'geojson',
    data: {
    type: 'FeatureCollection',
    features: features.map(({ id, type, properties, geometry }) => ({
        id,
        type,
        geometry,
        properties: {
        ...properties,
        yourProperty: JSON.parse(properties.yourProperty),
        }
    }))
    }
});

this.addLayer({ ... })

Then in your filter expression you can do

[
    'get',
    'prop1',
    ['at', 0, ['get', 'someProperty']]
]

An alternative to this if you have control of the data going into Mapbox is to format the properties like this instead:

properties: {
 'someProperty.length': 2,
 'someProperty[0].prop1' : 123,
 'someProperty[0].prop2' : 123,
 'someProperty[1].prop1' : 456,
 'someProperty[1].prop2' : 456,
}
viktor76525 commented 4 years ago

Is this working now? I make a geojson with properties "a":[1.1,2.2,...] Upload mbtitles And it appears to be a string. I cannot do ["at", 0, ["get", "a"]] Expected value to be of type array, but found string instead. Using mapbox gl js 1.11.0 after 1.10.1 wasn't working either. May just make 50 properties because the length is the same for each polygon. I see in mapbox studio that the properties are strings instead of arrays.

range-of-motion commented 3 years ago

Am I wrong to assume this will be "broken" for a while to come?

jayarjo commented 3 years ago

There should be at least a way to intercept and custom process the tile data. A way to insert a middleware basically. This should be very simple for you to implement I think.

derzwen commented 2 years ago

Whats the status here concerning the OP question? Is there a currently a workaround to get nested objects out of feature properties using query*Features?

Ojay commented 2 years ago

I've just run into this issue too, a whole 6 years later. Object/array data stored in my properties object (to be used and displayed on a pop-up) is being returned as a string. Any news or developments on how to handle this??

----- EDIT ----- Fixed by following @fc's technique and basically rebuilt the properties object in the appropriate place.


  const obj = {
    ...features[0],
    properties: {
      ...features[0].properties,
      arra1data: JSON.parse(features[0].properties.array1),
      array2data: JSON.parse(features[0].properties.array2),
      object1data: JSON.parse(features[0].properties.object1),
    },
  };
    setMarker(obj);
}```
eiiot commented 2 years ago

6 years later still not supported... running into the same issue here

zacBkh commented 1 year ago

Hello, I believe we still have this same old issue...?

RobJJ commented 1 year ago

Yeah, I think im experiencing the same/similar issue on a feature :: #11629

"properties": { ... "SCORES": { "2019": { .... }, ... } },

cant read the inner "2019" property when trying to set 'fill-color' as that object is showing as a string in the feature.

usefulthink commented 3 months ago

What I find interesting about this is that the rendering doesn't seem to care, so using nested properties seems to work just fine until you try to query the features (or use mouse-events). Would it be possible to update the documentation (geojson-source / get expression) to make this clear, or maybe throw an error?

Example here: https://codepen.io/usefulthink/pen/PorYQEK?editors=0010