Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
hsinister opened this issue Apr 13, 2016 · 41 comments
Open

Comments

@hsinister
Copy link

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
Copy link
Contributor

tmcw commented Apr 13, 2016

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

@lucaswoj
Copy link
Contributor

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

@ansis
Copy link
Contributor

ansis commented Apr 13, 2016

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:

  • for -native, will the geometry.hpp types support arrays and objects within feature properties?
  • should queries return the full geojson instead of the tiled geojson? this could be more useful, but it would be inconsistent with vector tile results.

@mcwhittemore
Copy link
Contributor

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
Copy link
Member

mourner commented Apr 14, 2016

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
Copy link

shkfnly commented May 14, 2016

Has there been any further resolution to this issue?

@gmaclennan
Copy link
Contributor

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
Copy link

shkfnly commented May 14, 2016

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

@gmaclennan
Copy link
Contributor

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

@gmaclennan
Copy link
Contributor

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
Copy link
Member

mourner commented May 16, 2016

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

@gmaclennan
Copy link
Contributor

@mourner WIP: mapbox/geojson-vt#59

sleepycat added a commit to sleepycat/old_usesthis that referenced this issue Jul 25, 2016
Stringifying was to get around this issue:
mapbox/mapbox-gl-js#2434

Removing since this is now resolved.
@lucaswoj lucaswoj changed the title #queryRenderedFeatures does not give the underlying JSON completely Support nested objects and arrays for GeoJSON features in query*Features Jul 29, 2016
@anandthakker
Copy link
Contributor

Note: I believe mapbox/vt-pbf#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
Copy link

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

@marr
Copy link

marr commented Sep 12, 2017

bump

@waissbluth
Copy link

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
Copy link
Contributor

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
Copy link

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

@anandthakker
Copy link
Contributor

@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
Copy link

janbaykara commented Feb 27, 2018

@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
Copy link
Contributor

👍 glad it's working!

@loganpowell
Copy link

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

@loganpowell
Copy link

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

@rebz
Copy link

rebz commented Nov 26, 2018

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
Copy link
Member

mourner commented Nov 26, 2018

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

@rebz
Copy link

rebz commented Nov 26, 2018

@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
Copy link
Contributor

fc commented May 30, 2019

@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
Copy link

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
Copy link

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

@jayarjo
Copy link

jayarjo commented Oct 18, 2021

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
Copy link

derzwen commented Dec 15, 2021

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
Copy link

Ojay commented Feb 22, 2022

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
Copy link

eiiot commented Apr 2, 2022

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

@zacBkh
Copy link

zacBkh commented May 10, 2023

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

@RobJJ
Copy link

RobJJ commented Oct 5, 2023

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
Copy link

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests