Clusters, and markers randomly loading onPageLoad *MapBox* - javascript

So what I want is for all the clusters and markers to be visible from the start. that is, when entering the page. But that's not the case.
Everything that should be on the map loads completely sporadically. Like when i hit hard refresh some times it suddenly wont load.
const bounds = Map.current
? Map.current.getMap().getBounds().toArray().flat()
: null;
The code block above might be the issue. When the map is empty of markers and clusters this code block logs null aswell. How can i use this in useEffect if that could work?
Cluster code:
const points = useMemo(
() =>
data.map((store) => ({
type: "Feature",
properties: {
cluster: false,
storeId: store.id,
category: store.country_code,
},
geometry: {
type: "Point",
coordinates: [store.coords[0], store.coords[1]],
},
})),
[data]
);
const bounds = Map.current
? Map.current.getMap().getBounds().toArray().flat()
: null;
const { clusters } = useSupercluster({
points,
bounds,
zoom: viewport.zoom,
options: { radius: 75, maxZoom: 20 },
});
Before and after refresh/move on map/saving code/pretty much anything
So when i console log this:
Before:
After:

#Kruzt are you using Mapbox GL JS? In that case, I'm not sure there's a need for the supercluster library. Have you tried something like this example? https://docs.mapbox.com/mapbox-gl-js/example/cluster/

Related

Own geojson data in Mapbox are not displayed

I am trying to fill my mapbox with data from a database. I'm building a geojson from data and I'm using this as the source in the map. However, I get the error in the debugger:
ErrorĀ {message: "Cannot read properties of undefined (reading
'length')"}
. If I use Mapbox's example URL: https://docs.mapbox.com/mapbox-gl-js/assets/earthquakes.geojson everything works without errors.
const getData = useCallback(async () => {
const points = Promise.all(
alleArtikel?.map((artikel) => ({
type: "Feature",
properties: {
userId: artikel.userId,
userBild: artikel.userBild,
artikelId: artikel._id,
anzahl: alleArtikel?.length,
},
geometry: {
type: "Point",
coordinates: [
parseFloat(artikel.long).toFixed(4),
parseFloat(artikel.lat).toFixed(4),
],
},
}))
);
setPoints(await points);
}, []);
useEffect(() => {
if (points.length)
map.on("load", () => {
map.addSource("users", {
type: "geojson",
data: { points }, //message in Debugger: "Cannot read properties of undefined (reading 'length')"
cluster: true,
clusterMaxZoom: 14,
clusterRadius: 50,
});
...
})
}, [points]);
Your code snippet seems a bit incomplete (what is setPoints() for instance), but two things look wrong:
An array of Feature is not a valid GeoJSON object. You need to wrap it in a FeatureCollection object. See the GeoJSON spec.
data: { points } looks wrong: that would pass an object with a single member called points.
You probably want something like:
data: {
type: 'FeatureCollection',
features: points
}

Can't add a geoJson layer to my Open Layers map

Using Open Layers and leaflet-sidebar-v2, I've added the sidebar to my map, this works. However, I also need to add another layer to my map, this layer will outline each country. I have the coordinates stored in a 'borders.json' file. I'm attempting to use D3.json to to import the border coordinates and then L.geoJson to add the new layer to my map.
I'm currently getting the following error message:
Uncaught TypeError: t.getLayerStatesArray is not a function
Here is the relevant part of my code..
var map = new ol.Map({
target: "map",
layers: [
new ol.layer.Tile({
source: new ol.source.OSM(),
}),
],
view: new ol.View({
center: ol.proj.transform([7, 51.2], "EPSG:4326", "EPSG:3857"),
zoom: 3,
}),
});
var sidebar = new ol.control.Sidebar({
element: "sidebar",
position: "left",
});
map.addControl(sidebar);
d3.json(("borders.json"), function (json){
function style(feature) {
return {
fillColor: "transparent",
weight: 1,
opacity: 0.4,
color: 'grey',
fillOpacity: 0.3
}
}
geojson = L.geoJson(json, {
style: style,
}).addTo(map);
})
I think I might be adding the geojson layer to my map incorrectly, but I can't figure out what is wrong. I've spent quite a bit of time playing with it, but no luck.
Any helps is appreciated.
Cheers,
Beat
It might be hard to tell what the problem is without knowing other possible relevant parts of your code. I'd start by checking that the contents of borders.json follows valid GeoJSON format.
This is likely unrelated to your question, but is there a reason that you've declared style as a function like function style(feature) { ... }?
It looks like the style attribute of L.geoJson accepts an object rather than a function.

Do I need to add a source to my map in order to use clustering?

I want to add clustering to my map from mapbox, I followed the getting started as they suggest:
https://www.mapbox.com/install/js/cdn-add/
And now I have a map, I also added some of my own markers, which are users from my backend that have lats and longs.
So I have a map and some markers.
Now, I want to add clustering, and in the example:
https://docs.mapbox.com/mapbox-gl-js/example/cluster/
they add a source:
map.addSource("earthquakes", {
})
which I don't have, and at this moment I don't think I even need. because I can see the tiles and my markers.
I thought about adding a source, but which source? I don't need a source, I already have what I needed, the tiles and the markers... but the cluster option is in the addSource method on the map. So.. I'm lost here.
This is what they show in the example, but as I said, I don't have or need any other source, as I already see the tiles and my markers, I just want to cluster.
map.addSource("earthquakes", {
type: "geojson",
data: "https://docs.mapbox.com/mapbox-gl-js/assets/earthquakes.geojson",
cluster: true,
clusterMaxZoom: 14, // Max zoom to cluster points on
clusterRadius: 50 // Radius of each cluster when clustering points (defaults to 50)
});```
I found the solution,
I had to create a geoJson object from my users like this:
const geoJsonMarkers = users.map( (place, i ) => {
const [placeLng, placeLat] = place.location.coordinates;
const position = { lat: placeLat, lng: placeLng };
return {
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [position.lng, position.lat]
},
"properties": {
"name": place.name
}
}
})
const usersArray = {
"features": geoJsonMarkers
}
And then pass it to the data property of the source like so:
map.on('load', function() {
// Add a new source from our GeoJSON data and set the
// 'cluster' option to true. GL-JS will add the point_count property to your source data.
map.addSource("users", {
type: "geojson",
// Point to GeoJSON data. This example visualizes all M1.0+ earthquakes
// from 12/22/15 to 1/21/16 as logged by USGS' Earthquake hazards program.
data: usersArray,
cluster: true,
clusterMaxZoom: 14, // Max zoom to cluster points on
clusterRadius: 50 // Radius of each cluster when clustering points (defaults to 50)
});
...
the addSource name is arbitrary, could be anything in my case are users so I called it users.

eCharts 3 - zoom on geo map onclick

Does anyone know how to click on a country, then have it zoom in on that country/area in a 2D geo world map?
I am using the world 2D map that is provided in eCharts 3. So my options look like this:
geo: {
name: '2D Global Map',
type: 'map',
map: 'world',
roam: true,
label: {
emphasis: {
show: false
}
},
...
Here is what I have thus far. When I click it just zooms straight in to 4 rather than where I clicked.
myChart.on('click', function (params) {
myChart.setOption({
geo: {
zoom: 4
}
});
});
myChart.setOption(option);
I have tried to find ways to zoom in on the x and y offsets, but that doesn't work.
I also tried to center the map first like this but you need the latitude and longitude of where to center the map inside the array. The map uses JSON for the coordinates, and I can see them, but I can't get them to pull into the array.
myChart.on('click', function (params) {
myChart.setOption({
geo: {
center: [(need to get lat/long of where clicked)],
zoom: 4
}
});
});
myChart.setOption(option);
Any thoughts on ways to do this?
Just in case anyone else is having this same problem, I was able to solve it in this manner:
myChart.on('click', function(params) {
if (params.data) {
myChart.setOption({
geo: {
center: params.data.value,
zoom: 6
}
});
} else {
myChart.setOption({
geo: {
center: [0,0],
zoom: 1
}
});
}
myChart.setOption(option);
});
It doesn't fully solve the problem where I wanted to be able to zoom in on any country, but it does solve the problem in the fact that I can zoom into a data point inside a country.
If anyone else has suggestions on how to do the country part please let me know.

Styling MarkerClusterer Icons?

I am using the MarkerCluster.js to create clustering in my google maps api. The clusters work how I want them however I want to style differently than yellow, blue and red circles. I was trying to use the MarkerStyleOptions and it says you have an array of styles with the smallest cluster icon first and the biggest last. I tried to create this below but I am getting really confused about what syntax to use and can't find any good examples.
var clusterStyles = [
[opt_textColor: 'white'],
[opt_textColor: 'white'],
[opt_textColor: 'white']
];
var mcOptions = {
gridSize: 50,
styles: clusterStyles,
maxZoom: 15
};
var markerclusterer = new MarkerClusterer(map, cluster, mcOptions);
What you need to do is use the url to specify which images to use instead of the blue/yellow/red images currently being used. And probably a good idea to include the height and width options too.
var clusterStyles = [
{
textColor: 'white',
url: 'path/to/smallclusterimage.png',
height: 50,
width: 50
},
{
textColor: 'white',
url: 'path/to/mediumclusterimage.png',
height: 50,
width: 50
},
{
textColor: 'white',
url: 'path/to/largeclusterimage.png',
height: 50,
width: 50
}
];
It's never too late to post a rather helpful answer, so additionally you can look through the whole MarkerClusterer Documentation for IconStyle
UPDATE
There's also google maps v3 utility on github as stated by ehcanadian
According to latest docs, renderer does the trick. It allows you to use a Marker w/all its styling options - see Marker documentation:
new MarkerClusterer({
renderer: {
render: ({ markers, _position: position }) => {
//here is where you return a Marker
//and style it w/custom label/icon props
return new google.maps.Marker({
position: {
lat: position.lat(),
lng: position.lng(),
},
label: String(markers.length),
});
},
},
...etc
});
You can now pass your own renderer to your MarkerClusterer Object which returns a google.maps.Marker like this:
const renderer = {
render({ count, position }) {
return new google.maps.Marker({
label: { text: String(count), color: "white", fontSize: "10px" },
position,
// adjust zIndex to be above other markers
zIndex: Number(google.maps.Marker.MAX_ZINDEX) + count,
});
}
}
// use this if you use NPM version
const markerCluster = new MarkerClusterer({ map, markers, renderer });
// Use this if you use the CDN version
// const markerCluster = new markerClusterer.MarkerClusterer({ markers, map , renderer});
Inside the render method, you can customize the cluster maker the same way you would customize normal marker. See https://developers.google.com/maps/documentation/javascript/custom-markers for more details. For example, if you just want to add a specific icon.png you could do it like this:
const icon = {
url: '/path/to/your/icon.png',
scaledSize: new google.maps.Size(50, 50),
};
const renderer = {
render({ count, position }) {
return new google.maps.Marker({
label: { text: String(count), color: "white", fontSize: "10px" },
position,
icon,
// adjust zIndex to be above other markers
zIndex: Number(google.maps.Marker.MAX_ZINDEX) + count,
});
}
}
You can also find some clustor marker render example at https://googlemaps.github.io/js-markerclusterer/public/renderers/
See https://developers.google.com/maps/documentation/javascript/marker-clustering for a full example how to setup MarkerCluster.
History Details:
The MarkerCluster library has been rewritten in 2021. One goal was to change the render process. The cluster icon itself is now a google.maps.Marker and can thus be styled just as normal marker. The mcOptions in the constructor is gone.
The proposed design is to encapsulate the logic for clustering and rendering into interfaces that the developer can extend specifically algorithm and renderer. The algorithm computes the clusters and the renderer generates a google.maps.Marker to represent clusters instead of using a google.maps.OverlayView.
https://github.com/googlemaps/js-markerclustererplus/issues/300
https://github.com/googlemaps/js-markerclustererplus/issues/293
https://docs.google.com/document/d/1X8hQaHdVvpB-12bOQ_qbgfGFet8gPRTtHyO_EihcpsA/edit

Categories