Is there any way of clustering points by field from properties object?
I am using react-map-gl library.
https://visgl.github.io/react-map-gl/examples/clusters
Example feature object:
{
type: "Feature",
geometry: {
type: "Point",
coordinates: cords,
},
properties: {
...item,
layer_id: item.id,
name: item.name,
FIELD_TO_CLUSTER_BY: example
},
};
<Source
id="markers"
type="geojson"
data={features}
cluster={true}
clusterMaxZoom={14}
clusterRadius={50}
clusterProperties={{
}}
>
<Layer {...clusterLayer} />
<Layer {...unclusteredPointLayer} />
</Source>
In radius of 50 i would like to cluster all markers that FIELD_TO_CLUSTER_BY equals to 'something'
I was thinking about grouping data by FIELD_TO_CLUSTER_BY and create separate source and layer for each of them.
So the solution that i found (don't know if it is best) is to group all points by FIELD_TO_CLUSTER_BY property and to display them i needed to loop through grouped arrays and for each of them i created new and
Related
I'm looking for a way to access a nested array in my data structure.
My objects in my data array look like this :
{
name: name,
logo: url,
categories: [
{
name: Entertainment,
slug: "entertainment
},
{
name: Kids,
slug: kids
},
]
}
In the Docs I states that I need to use columnHelper.accessor to extract primitive values for each item in your data array.
My question is : how can I configure my accessor on "categories" to display both of them in my cell (using map I guess) ?
First, you don't need to use columnHelper; you can, for convenience (well, type safety, mostly).
You can do something like this:
cell: info => {
const value = info.getValue() as YourCategoryType[]; //casting may not be required here
return <>{value.map(v => <p key={v.slug}>{v.name}</p>)}</>
},
I'm using mapbox-gl and mapbox gl draw.
On the map, there will be a layer which has all markers. And I implemented polygon draw on that map using mapbox-gl-draw.
After that, I use turf.js to get the points within the drawn pologon. So, now, I have those points.
But those points are just points, not fully features. I want to query that features using those points.
Let's say I get these coordonites points as a result of turf.
[
[2, 2],
[3, 3],
[4, 4]
]
On the above mentioned layer, I've added markers and one of those looks like below:
{
type: "Feature",
properties: {
title: name,
id: id,
},
geometry: { coordinates: [long, lat], type: "Point" },
}
So, how can I get those features using above points?
This data-wrangling task comes up a lot in spatial web development. All you need to do is build out a geojson Point feature for each set of coordinates. You can use Array.map() for this:
const pointFeaturesArray = coordinatesArray.map((coordinates) => {
return {
type: 'Feature',
geometry: {
type: 'Point',
coordinates: coordinates
},
properties: {}
}
})
pointFeaturesArray is now an array of valid geojson point features, but what you probably want is a FeatureCollection (an array of geojson features is not valid geojson on its own). To do that, just set this arrray to the features property in a FeatureCollection:
const pointsFC = {
type: 'FeatureCollection',
features: pointFeaturesArray
}
You may want to test this geojson to make sure it is valid. One way to do this is to log it to the console, then copy and paste it into geojson.io. If it is valid it will appear on the map, if it is not valid, you will see some red highlighting in the code editor area telling you where something is wrong.
pointsFC is now ready to use in map.addSource():
map.addSource('mypointfeatures', {
type: 'geojson',
source: pointsFC
}
This is my first stackoverflow post....be kind
I am building a react app where I want to display an image from a array of objects but the "images:" object has two objects inside example:
[
{ value: "all", label: "All sets" },
{
value: "base1",
label: "Base",
series: "Base",
printedTotal: 102,
total: 102,
legalities: { unlimited: "Legal" },
ptcgoCode: "BS",
releaseDate: "1999/01/09",
updatedAt: "2020/08/14 09:35:00",
images: {
symbol: "https://images.pokemontcg.io/base1/symbol.png",
logo: "https://images.pokemontcg.io/base1/logo.png",
},
},]
my question is how would I access the inner object to display just the "logo:" object?
my assumption is to use a combination of .map and Object.keys() but I'm unsure how to do it.
mycode is below...
import React from "react";
import setOptions from "../data/sets";
export default function SetOverview() {
const sets = setOptions;
return (
<div>
{sets.map((set) => {
return (
<div key={set.value}>
<h2>{set.label}</h2>;<p>Number of cards: {set.printedTotal}</p>
<p>Release date: {set.releaseDate}</p>
</div>
// image to be placed here
);
})}
</div>
);```
ma friend. Welcome to Stack Overflow, and no- you will not be treated with kindness. That being said,
If you are sure about the keys that are being passed in the dataset, i.e.: objName.objItem.images.logo, then all you need to do is,
Check if the images key exists in that object (because your first object doesn't have it, so I suspect there may be a reason for that).
Load the image's logo value inside that div you've specified.
To achieve this, all you need to do is:
set.images?.logo && <img src={set.images.logo} />
And voila, you shall have your image. The question mark checks if key exists.
I am trying to adapt the example Display HTML clusters with custom properties for react-map-gl.
I got basic clusters without custom styling working (adapted from Create and style clusters):
<ReactMapGL ref={mapRef}>
<Source id="poi-modal-geojson" type="geojson" data={pointsToGeoJSONFeatureCollection(points)}
cluster={true}
clusterMaxZoom={14}
clusterRadius={50}
>
<Layer {...{
id: 'clusters',
type: 'circle',
source: 'poi-modal-geojson',
filter: ['has', 'point_count'],
paint: {
'circle-color': [
'step',
['get', 'point_count'],
'#51bbd6',
100,
'#f1f075',
750,
'#f28cb1'
],
'circle-radius': [
'step',
['get', 'point_count'],
20,
100,
30,
750,
40
]
}
}} />
<Layer {...{
id: 'unclustered-point',
type: 'circle',
source: 'poi-modal-geojson',
filter: ['!', ['has', 'point_count']],
paint: {
'circle-color': '#11b4da',
'circle-radius': 4,
'circle-stroke-width': 1,
'circle-stroke-color': '#fff'
}
}} />
</Source>
</ReactMapGL>
Here, pointsToGeoJSONFeatureCollection(points: any[]): GeoJSON.FeatureCollection<GeoJSON.Geometry> is a function returning a GeoJSON (adapted from here).
However, I need more complex styling of markers and I am trying to adapt Display HTML clusters with custom properties without success so far. I mainly tried to adapt updateMarkers() and to call it inside useEffect():
const mapRef: React.Ref<MapRef> = React.createRef();
const markers: any = {};
let markersOnScreen: any = {};
useEffect(() => {
const map = mapRef.current.getMap();
function updateMarkers() {
const newMarkers: any = {};
const features = map.querySourceFeatures('poi-modal-geojson');
// for every cluster on the screen, create an HTML marker for it (if we didn't yet),
// and add it to the map if it's not there already
for (const feature of features) {
const coords = feature.geometry.coordinates;
const props = feature.properties;
if (!props.cluster) continue;
const id = props.cluster_id;
let marker = markers[id];
if (!marker) {
let markerProps = {
key: 'marker' + id,
longitude: coords[0],
latitude: coords[1],
className: 'mapboxgl-marker-start'
}
const el = React.createElement(Marker, markerProps, null),
marker = markers[id] = el;
}
newMarkers[id] = marker;
if (!markersOnScreen[id]) {
// TODO re-add
// marker.addTo(map);
}
}
// for every marker we've added previously, remove those that are no longer visible
for (const id in markersOnScreen) {
if (!newMarkers[id]) delete markersOnScreen[id];
}
markersOnScreen = newMarkers;
}
// after the GeoJSON data is loaded, update markers on the screen on every frame
map.on('render', () => {
if (!map.isSourceLoaded('poi-modal-geojson')) return;
updateMarkers();
});
}, [points]);
Unfortunately, the Marker created using React.createElement() isn't displayed I am not sure what is the right approach to create Marker elements in updateMarkers() or if my approach is completely wrong.
There is a great article on marker clustering which uses the supercluster and use-supercluster libraries and it makes clustering really easy not only for map box but for other map libraries as well, you can find it here.
You just have to convert your points into GeoJSON Feature objects in order to pass them to the useSupercluster hook and for the calculations to work. It will return an array of points and clusters depending on your current viewport, and you can map through it and display the elements accordingly based on the element.properties.cluster flag.
The properties property of the GeoJSON Feature object can be custom so you can pass whatever you need to display the markers later on when you get the final cluster array.
I have a <List /> inside an <InfiniteLoader />, inside an <AutoSizer />, also <WindowScroller /> and <WindowScroller /> 😁 (wow, so much hocs there) but for simplicity, I think my question could fit the same with a simple <List /> Component.
I'm not sure if there is a way to render some kind of separator or heading like a title for the section (piece of data) rendered below.
Since each item have a prop that allow to group the data in chunks, and I am receiving this data ordered and grouped like:
[
{
item: 1,
name: 'Banana',
kind: 'fruits',
},
{
item: 2,
name: 'Apple',
kind: 'fruits',
},
{
item: 3,
name: 'Watermelon',
kind: 'fruits',
},
{
item: 4,
name: 'Dog',
kind: 'animals',
},
{
item: 5,
name: 'Cat',
kind: 'animals',
},
{
item: 6,
name: 'Horse',
kind: 'animals',
},
//...
]
the idea is to render something like:
<ul>
<li className="fullWidth">
<h3>Fruits</h3>
</li>
<li>Banana</li>
<li>Apple</li>
<li>Watermelon</li>
<li className="fullWidth">
<h3>Animals</h3>
</li>
<li>Dog</li>
<li>Cat</li>
<li>Horse</li>
</ul>
Making some calculation in rowRenderer method?
Or, since I am wrapping the <List /> in an <InfiniteLoader /> I could pass an argument when fire loadMoreRows, but anyway I think I have to do some calculation in rowRenderer, the responsible for the final render.
This is possible, although much easier if your data is pre-sorted so that groups aren't interleaved. You basically have 2 options:
Pre-flatten your data into an array of 2 types of items. One type is a header and the other type is a child. If you want headers to be a different size than regular rows you'll need to also provide a rowHeight getter function that's able to distinguish between a header item and a child and return a different value. Your rowRenderer will also need to do the same.
Compare the current datum's "kind" to the one that came before it and include a header with the rendered row if they are different. This approach also requires a custom rowHeight getter but does not require any data flattening so it may be easier/faster. The only downside is that your header and row item will be within the same div (or li) if you approach this way.
Here's an example of option 2: https://plnkr.co/edit/l22Ir7?p=preview
And here is the relevant code bits:
function includeHeader(index) {
return (
index === 0 ||
list[index].kind !== list[index - 1].kind
);
}
function rowHeight({ index }) {
return includeHeader(index)
? ROW_HEIGHT_WITH_HEADER
: ROW_HEIGHT;
}
function rowRenderer({ index, isScrolling, key, style }) {
const datum = list[index];
return (
<div
className='Row'
key={key}
style={style}
>
{includeHeader(index) && (
<h3>{...}</h3>
)}
<div>{...}</div>
</div>
);
}