After figuring out how to deal with TypeScript and use-supercluster library I have "made it work" until I got a new problem: I get an empty array whenever I use useSuperCluster() function.
I am following the creator's guide so I can handle my own project.
This is what I do:
const [bounds, setBounds] = useState(undefined as BBox | undefined);
const { data, error } = useSwr(API_URL, fetcher);
const coords: Array<ParkingData> = data && !error ? data.data : [];
const points: Array<PointFeature<GeoJsonProperties>> = coords.map(pd => ({
type: "Feature",
properties: {
cluster: false,
pdId: pd._id,
category: 'test'
},
geometry: { type: "Point", coordinates: [ pd.lat, pd.lng ] }
}));
const { clusters } = useSuperCluster({
points,
bounds,
zoom,
options: { radius: 75, maxZoom: 25 }
});
When I debug points I get something like:
But then, clusters is empty. I update bounds like in the video with a onChange attribute, like:
onChange={({ zoom, bounds }) => {
setZoom(zoom);
setBounds([
bounds.nw.lng,
bounds.se.lat,
bounds.se.lng,
bounds.nw.lat
]);
}}
So, what am I doing wrong?
Edit:
I had added supercluster object to useSuperCluster() destructuring like const { clusters, supercluster } = useSuperCluster(...) and after debugging it I get the following object:
Try changing this line order:
geometry: { type: "Point", coordinates: [ pd.lat, pd.lng ] }
to:
geometry: { type: "Point", coordinates: [ pd.lng, pd.lat ] }
apparently, the order is that way, in my case i tried this change and it didn't work for me, but for you there's a chance it will work.
https://github.com/mapbox/supercluster/issues/45
Related
I am have a vuejs/nuxtjs application using the gmap-vue package which is a fork of vue-google-maps. I have followed the drawing manager with slot example which is working as shown on the document, good times!
But...
I would like to do two more things
Save the data that I added to the map, how do I access it?
How to load geojson onto the map and then edit it.
Examle below:
More information from further investigation.
I am trying to build up data in the form of geojson
let shapes = [];
for(let shape in this.shapes){
let tmp = {
"type": "Feature",
"properties": {
"id": this.shapes[shape].id || null,
"zIndex": this.shapes[shape].zIndex || null
},
"geometry": {
"type": this.shapes[shape].type,
"coordinates": // where to find shape coordinates?
}
};
shapes.push(tmp)
}
console.log(JSON.stringify(shapes));
displays the following in the console.log
[
{
"type":"Feature",
"properties":{
"id":null,
"zIndex":null
},
"geometry":{
"type":"polygon",
"coordinates": // where to find coordinates?
}
}
]
console.log[this.shapes[shape]];
returns
{__ob__: Observer}
overlay: (...)
type: (...)
How can I access the coordinates?
For anyone else looking for an answer, I hope this helps you out.
const newShapes = [];
this.shapes.forEach((shape) => {
const coords = [];
shape.overlay.latLngs.getArray().forEach((latLng) => {
coords.push([latLng.lat, latLng.lng]);
});
newShapes.push({
type: 'Feature',
geometry: {
type: shape.type,
coordinates: coords,
},
});
});
As shown here: https://diegoazh.github.io/gmap-vue/#getting-a-map-reference
this.$refs.mapRef.$mapPromise.then((map) => {
map.panTo({lat: 1.38, lng: 103.80})
})
is working pretty great.
<gmap-drawing-manager ref="drawingRef">
and
<gmap-drawing-manager :shapes="shapes" ref="drawingRef">
allows to access the same information with this.shapes!
I am following an example provided on the deck.gl github repository that displays polygons from a geojson.
I've since changed the initial focus of the map and provided my own geojson to visualise, the data I've replaced the examples with has a temporal component that I'd like to visualise via the manipulation of a range input.
Example GeoJSON Structure
{
"type": "FeatureCollection",
"name": "RandomData",
"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } },
"features": [
{ "type": "Feature",
"properties": { "id": 1,"hr00": 10000, "hr01": 12000, "hr02": 12000, "hr03": 30000, "hr04": 40000, "hr05": 10500, "hr06": 50000}, "geometry": { "type": "Polygon", "coordinates": [ [ [ 103.73992, 1.15903 ], [ 103.74048, 1.15935 ], [ 103.74104, 1.15903 ], [ 103.74104, 1.15837 ], [ 103.74048, 1.15805 ], [ 103.73992, 1.15837 ], [ 103.73992, 1.15903 ] ] ] } } ] }
Instead of repeating each geometry for every timepoint I've shifted the temporal aspect of the data to the properties. This makes the file size manageable on the complete dataset (~50mb versus ~500mb).
For visualising a single time point I know that I can provide the property to getElevation and getFillColor.
_renderLayers() {
const {data = DATA_URL} = this.props;
return [
new GeoJsonLayer({
id: 'geojson',
data,
opacity: 0.8,
stroked: false,
filled: true,
extruded: true,
wireframe: true,
fp64: true,
getElevation: f => f.properties.hr00,
getFillColor: f => COLOR_SCALE(f.properties.hr00),
getLineColor: [255, 255, 255],
lightSettings: LIGHT_SETTINGS,
pickable: true,
onHover: this._onHover,
transitions: {
duration: 300
}
})
];
}
So I went ahead and used range.slider, adding code to my app.js, this following snippet was added. I believe I also may be placing this in the wrong location, should this exist in render()?
import ionRangeSlider from 'ion-rangeslider';
// Code for slider input
$("#slider").ionRangeSlider({
min: 0,
max: 24,
from: 12,
step: 1,
grid: true,
grid_num: 1,
grid_snap: true
});
$(".js-range-slider").ionRangeSlider();
added to my index.html
<input type="text" id="slider" class="js-range-slider" name="my_range" value=""/>
So how can I have the slider change which property of my geojson is being supplied to getElevation and getFillColor?
My JavaScript/JQuery is lacking and I have been unable to find any clear examples of how to change the data property based on the input, any help is greatly appreciated.
Here is a codesandbox link - doesn't seem to like it there however.
Locally with npm install and npm start should have it behave as intended.
At first you'll need to tell your dependent accessors about the value that is going to be changed by the slider. This can be done by using updateTriggers:
_renderLayers() {
const { data = DATA_URL } = this.props;
return [
new GeoJsonLayer({
// ...
getElevation: f => f.properties[this.state.geoJsonValue],
getFillColor: f => COLOR_SCALE(f.properties[this.state.geoJsonValue]),
updateTriggers: {
getElevation: [this.state.geoJsonValue],
getFillColor: [this.state.geoJsonValue]
}
// ...
})
];
}
And to actually change this value using range-slider you need to add onChange callback during the initialization:
constructor(props) {
super(props);
this.state = { hoveredObject: null, geoJsonValue: "hr01" };
this.sliderRef = React.createRef();
this._handleChange = this._handleChange.bind(this);
// ...
}
componentDidMount() {
// Code for slider input
$(this.sliderRef.current).ionRangeSlider({
// ...
onChange: this._handleChange
});
}
_handleChange(data) {
this.setState({
geoJsonValue: `hr0${data.from}`
});
}
render() {
...
<DeckGL ...>
...
</DeckGL>
<div id="sliderstyle">
<input
ref={this.sliderRef}
id="slider"
className="js-range-slider"
name="my_range"
/>
</div>
...
}
And this is basically it. And here is the full code
I am running into some difficulties to remove a geojon polygon from a layer with mapboxGL.
Here the following code to add my geoson polygons where
array_geo is a polygon geometry. Adding the polygon works like a charm,
but I still did not find how to delete my polygons. The only trick I found is to use the removeLayer(id) function which delete the entire layer.
var id="mylayer";
itemObj = {
type:"Feature",
properties: {
description: description,
type: 23
},
geometry: {
type: "Polygon",
coordinates: array_geo
},
};
mpolygons.push(itemObj);
map.addSource(id,{
"type":"geojson",
"data":{
"type":"FeatureCollection",
"features":mypolygons
}
self.map.addLayer({
"id": id,
"source":id,
"type":"fill",
"layout": {'visibility':'visible'},
"paint": {
'fill-color': '#088',
//'fill-opacity': 0.8
}
})
Thanks !
I'm trying to add cities on a county map. The lat and long are drawn corectcly on the full size map(Romania level) but on the county map these are drawn wrong. I think I should rescale the map or lat/long of the city, I don't know...
How should I generate the county map in accordance with cities lat/long?
The code is listed below:
http://jsfiddle.net/xzAx7/33/
var map;
AmCharts.ready(function() {
map = new AmCharts.AmMap();
map.pathToImages = "http://www.ammap.com/lib/images/";
//map.panEventsEnabled = true; // this line enables pinch-zooming and dragging on touch devices
map.balloon.color = "#000000";
var wordlDataProvider = {
mapVar: AmCharts.maps.Cluj,
getAreasFromMap: true,
areas: [
{ id: "FR", color: "#4444ff" },
{ id: "RU", color: "#4444ff" },
{ id: "US", color: "#4444ff" }
],
images: [{
title: "ClujNapoca",
latitude: 46.85307355,
longitude: 23.63327696,
type: "circle",
color: "red",
scale: 0.5
}, {
title: "Turda",
latitude: 46.5745618,
longitude: 23.78573862,
type: "circle",
color: "red",
scale: 0.5
}]
};
map.dataProvider = wordlDataProvider;
map.areasSettings = {
autoZoom: true,
selectedColor: "#CC0000"
};
map.write("mapdiv");
});
I've found the solution. You need to define "defs" properties (leftLongiitude, ...) when you create a custom map. This is specified in this article - https://www.amcharts.com/tutorials/creating-custom-maps-for-javascript-ammap/
In the amCharts article is explained how you can get these "defs" properties.
I've changes the code below to understand how it works
"defs": {
"amcharts:ammap": {
"projection":"mercator",
"leftLongitude":"22.607117",
"topLatitude":"47.363798",
"rightLongitude":"24.261932",
"bottomLatitude":"46.384241"
}
}
demo: http://jsfiddle.net/xzAx7/35/
I'm trying to integrate Cesium 1.11 with an existing backend sending GeoJSON. I'm able to load data onto the view successfully from the first message, however subsequent calls to load() do not update the display.
I've simplified the problem to the following, also available as a fiddle.
I expect the second load call to update the display to move the marker to New York, however it stays on London.
Feature window still shows the "foo" property as 123, I expect 456.
Code
var viewer = new Cesium.Viewer('cesiumContainer');
var source = new Cesium.GeoJsonDataSource("name123");
viewer.dataSources.add(source);
source.load({
type: "FeatureCollection",
crs: {
type: "name",
properties: {
name: "urn:ogc:def:crs:OGC:1.3:CRS84"
}
},
features: [{
type: "Feature",
properties: {
foo: 123,
},
geometry: {
type: "Point",
coordinates: [0.1275, 51.5072] // London
},
id: "123"
}]
});
// workaround, but has side effect of destroying feature window
// source.entities.removeAll();
// sometime later...
source.load({
type: "FeatureCollection",
crs: {
type: "name",
properties: {
name: "urn:ogc:def:crs:OGC:1.3:CRS84"
}
},
features: [{
type: "Feature",
properties: {
foo: 456,
},
geometry: {
type: "Point",
coordinates: [-75.1890, 42.3482] // New York
},
id: "123"
}]
});
What I've tried
"forced" an update by calling source.entities.removeAll() however this has the side effect of closing the feature window if it is open during the the update. I'm receiving messages every second so this is not desirable.
Yes, I'm aware of the proprietary CZML system, however I'd like to stick to GeoJSON for this relatively simple system.
Update: further debugging. The problem appears to be a design feature...
load() helper method in GeoJsonDataSource calls that._entityCollection.removeAll(). This is between a suspendEvents() and resumeEvents() so does not cause the feature window to close.
After the resumeEvents() "change" events are fired even though the entities have actually been recreated
The existing BillboardVisualizer created by Cesium.Viewer keeps a cached instances to the Entities it used the first time it rendered
BillboardVisualizer.update() keeps reading the first position from the 'stale' entity instances and therefore no update is seen.
This looks like a bug in Cesium I just submitted issue #2891 and will try and get a fix into the 1.12 release on August 3rd. In the meantime, you should be able to workaround the issue using your removeAll strategy combined with resetting the selected entity after the load (which should keep the InfoBox around, which is what I assume you mean by feature window.) Here's a complete example that you can based into Sandcastle to see it in action.
var viewer = new Cesium.Viewer('cesiumContainer');
var source = new Cesium.GeoJsonDataSource("name123");
viewer.dataSources.add(source);
Sandcastle.addToolbarButton('Load 1', function(){
source.entities.removeAll();
source.load({
type: "FeatureCollection",
crs: {
type: "name",
properties: {
name: "urn:ogc:def:crs:OGC:1.3:CRS84"
}
},
features: [{
type: "Feature",
properties: {
foo: 123
},
geometry: {
type: "Point",
coordinates: [0.1275, 51.5072] // London
},
id: "123"
}]
}).then(function(){
viewer.selectedEntity = source.entities.values[0];
});
});
Sandcastle.addToolbarButton('Load 2', function() {
source.entities.removeAll();
source.load({
type: "FeatureCollection",
crs: {
type: "name",
properties: {
name: "urn:ogc:def:crs:OGC:1.3:CRS84"
}
},
features: [{
type: "Feature",
properties: {
foo: 456
},
geometry: {
type: "Point",
coordinates: [-75.1890, 42.3482] // New York
},
id: "123"
}]
}).then(function(){
viewer.selectedEntity = source.entities.values[0];
});
});
An alternative solution is to just use the GeoJsonDataSource for the creation of entities from JSON, and add the adding/updating manually into the viewer global entities collection
var viewer = new Cesium.Viewer('cesiumContainer');
var source = new Cesium.GeoJsonDataSource("name123");
// don't add source to viewer
source.load(...);
// sometime later...
source.load(...);
// manually update
viewer.entities.suspendEvents();
source.entities.values.forEach(function(entity) {
var existing = viewer.entities.getById(entity.id);
if (existing === undefined) {
viewer.entities.add(entity);
} else {
entity.propertyNames.forEach(function(name) {
existing[name] = entity[name];
});
}
}, this);
viewer.entities.resumeEvents();