Reputation: 1122
I want to show markers on a map, where each marker is a UserLocation
. A User
can have multiple UserLocation
.
When I cluster markers, I'd like to show the list of the Users of those clustered markers, without duplicates.
For instance, let's take these 3 near markers:
{ // Marker1
type: 'Feature',
properties: {user_id : "Daniele"},
geometry: { type : 'Point',
coordinates : [lng0, lat0]
}
},
{// Marker2
type: 'Feature',
properties: {user_id : "Daniele"},
geometry: { type : 'Point',
coordinates : [lng1, lat1]
}
},
{// Marker3
type: 'Feature',
properties: {user_id : "Roberto"},
geometry: { type : 'Point',
coordinates : [lng2, lat2]
}
}
When I cluster them, clicking the clustered circle, I want to see "Daniele, Roberto". How can I do that?
Moreover, I'd like to set the size of circle, according to the distinct number of different users clustered (in the example above, should be 2).
**UPDATE 2
JSFIDDLE <--
An idea could be build an array of distinct names, and then use the length
expression to size the circle.
Anyway, there should be a kind of syntax error...
clusterProperties: {
distinctNames :
['case',
/*cond */ ["!", ['in',['get', 'user_id'], ['accumulated']]],
/*result*/ ['concat', ['concat', ['get', 'user_id'], ',']],
/*default*/ ['accumulated']
]
}
Upvotes: 1
Views: 2173
Reputation: 161
The documentation is not really clear, but here is how I achieved this "distinct accumulate".
From the clusterProperties
definition:
A custom reduce expression that references a special ["accumulated"] value, e.g.:
{"sum": [["+", ["accumulated"], ["get", "sum"]], ["get", "scalerank"]]}
Which results the same as:
{"sum": ["+", ["get", "scalerank"]]}
In your case, you want to accumulate the user_id
property from your markers without duplicates.
The logic is to add the user_id
only if it has not already been added in the accumulated value.
clusterProperties: {
distinctUsers: [
// ['accumulated'] is the current value iterated during the reduce (the property is defined at [1])
// ['get', 'distinctCountries'] is the accumulated / concatenated string
[
// Concat accumulated value + current value if not present in accumulated
'concat',
['get', 'distinctUsers'],
[
'case',
['in', ['accumulated'], ['get', 'distinctUsers']], // If accumulated (user_id) has already been added
'', // Add EMPTY string
['concat', ', ', ['accumulated']], // Add the user_id (concatenated with a comma in your case)
],
],
['get', 'user_id'], // [1]: source marker property iterated in the custom reduce function
]
}
As Steve said in his answer, you could also wrap the user_id
in some unique character so you don't accidentally find user "rob" within another user "robin" for instance.
The source property ['get', 'user_id']
defined at [1]
would become :
['concat', '%', ['get', 'user_id'], '%']
Upvotes: 3
Reputation: 126295
According to the documentation you want to do something like this:
map.addSource(userData, {
id: 'user-locations',
type: 'geojson',
data: 'myuserdata.geojson',
cluster: true,
clusterProperties: {
names: ['concat', ['concat', ['get', 'user_id'], ',']]
}
}
Clustered points in your source will now have a property, names
which will contain the comma-separated (and comma-terminated) string of names.
Moreover, I'd like to set the size of circle, according to the distinct number of different users clustered (in the example above, should be 2)
That sounds...challenging. One way I can think of doing that would be writing a custom accumulator function along these lines:
[distinctNames, allNames]
where the first is an integer, and the second is a string.allNames
contains our current name, just return the array.[distinctNames + 1, allNames + thisName]
.Manipulating arrays like this in Mapbox GL expressions is possible, but pretty fiddly. You need to use ['literal', ...]
and ['at', ...]
The code would look something like this:
clusterProperties: {
names: ['concat', ['concat', ['get', 'user_id'], ',']],
distinctNames: [
['case', ['in', ['get', 'distinctNames'], ['at', ['accumulated'], 1]
['accumulated'],
['literal', ['+', ['at', ['accumulated'], 0], 1], ['concat', ['at', ['accumulated'], 1], ['get', 'distinctNames']]]
],
['concat', '%', ['get', 'user_id'], '%'] // wrap user ID in some unique character so we don't accidentally find user "rob" within another user "robin" for instance.
]
}
It's unclear from the documentation exactly how the accumulator function works, or how you access the current value. Their example implies that it would be ['get', <name of cluster property>]
although that seems a bit weird.
Upvotes: 3