Loading updated data with GeoJsonDataSource in Cesium.js - javascript

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();

Related

Gmap vue save polyline, polygon and load geojson onto google map

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!

use-supercluster gets empty array

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

How to assign a JSON string to a google pie chart data var

The problem I am facing is that in my web server I am sending a JSON as argument via render_template to my website where I want to use that JSON to show a google pie chart.
The problem is that if I assign the google pie chart data statically like this:
var data = new google.visualization.DataTable({
cols: [
{ id: "", label: "objeto", type: "string" },
{ id: "", label: "quantidade", type: "number" }
],
rows: [
{ c: [{ v: "Caixa 2" }, { v: 3 }] },
{ c: [{ v: "Caixa 3" }, { v: 3 }] },
{ c: [{ v: "Caixa 4" }, { v: 3 }] }
]
});
It works perfectly. On the other hand if I assign it dynamically with the JSON that I am receiving from my server like this:
var data = new google.visualization.DataTable({{json}});
It stops showing the google pie chart in my website.
The things I tried until now was litteraly adapting the JSON to the desired format by google charts because I thought that was the only problem, but now that it is in the required format and it works statically I do not know any way of assigning my received JSON to the data var.
This is my ideal function that I would like to work.
function drawChart() {
var data = new google.visualization.DataTable({{json}});
var options = {
title: 'gráfico Objeto/Quantidade',
is3D: true
};
var chart = new google.visualization.PieChart(
document.getElementById('piechart')
);
chart.draw(data, options);
}
Desired result:
http://prntscr.com/oejojv
Actual result:
http://prntscr.com/oejooe
The JSON string is being HTML-escaped. Assuming that you're using Flask (guessing based on your mention of render_template), you need to do something like {{json | safe}}.
Also, this assumes that you have total control over the content of this JSON, because you are otherwise susceptible to cross-site scripting attacks.

ArcGIS API for Javascript: FeatureLayerCollection doesn't displaye all the features

This image shows the result of my implementation.
The problem here is the feaure layer displayed in the map shows only one of the features passed in the code.
How have I done it?
Create a feature layer using new FeatureLayer(featureCollectionObject, options?).
Create a Query and QueryTask to request features from the arcgi server.
var selectQuery: Query = new Query();
selectQuery.returnGeometry = true;
selectQuery.where = "1=1";
selectQuery.outFields = ["NAME", "X", "Y"];
var queryTask_XZQH = new QueryTask(FL_XZQH_URL);
queryTask_XZQH.execute(selectQuery);
Define a event handler for "complete" of queryTask.
function onQueryTask_XZQHComplete(evt: object) {
console.log(evt.featureSet.geometryType);
//console.log(evt.featureSet);
FL_XZQH = new FeatureLayer({
featureSet: evt.featureSet,
layerDefinition: {
geometryType: "esriGeometryPolygon",
className: "xzqh",
objectIdField:"OBJECTID",
fields: [
{
name: "OBJECTID ",
type:"esriFieldTypeOID",
alias:"OBJECTID"
},
{
name: "ID ",
type:"esriFieldTypeInteger ",
alias:"Id"
},
{
name: "Name",
type: "esriFieldTypeString",
length: 50,
alias: "行政区划名称"
},
{
name: "X",
type: "esriFieldTypeDouble",
alias: "经度"
},
{
name: "Y",
type: "esriFieldTypeDouble",
alias: "纬度"
}
]
}
});
map.addLayer(FL_XZQH);
}
The result of QueryTask is fine, and the count of the features is 18.
However, when I use map.addLayer, the map just displays one feature.
The feature layer does not have a valid object ID. Make two changes to fix it:
Change this:
selectQuery.outFields = ["NAME", "X", "Y"];
To this (i.e. include the object ID in your query):
selectQuery.outFields = ["OBJECTID", "NAME", "X", "Y"];
Change this:
{
name: "OBJECTID ",
type:"esriFieldTypeOID",
alias:"OBJECTID"
},
To this (i.e. remove the space at the end of the field name):
{
name: "OBJECTID",
type:"esriFieldTypeOID",
alias:"OBJECTID"
},
Note: this will only work if the feature service actually has a field called OBJECTID.

Cannot create new javascript object

I am trying to use the StoreSeries with Dojo in order to create charts. However when I try to create the array by:
new StoreSeries(store, { query: { site: 1 } }, "value");
Then the javascript stops running and cannot continue to render the chart.
This is all the script that I think might be relevant - ask if you need to see any more.
function setupWeekElectricBar(Chart, theme, ClusteredColumns, Columns, Tooltip, Highlight, Observable, Memory, StoreSeries)
{
var data = [
{ id: 1, value: 5, site: 1 },
{ id: 2, value: 2, site: 1 },
{ id: 3, value: 3, site: 1 },
{ id: 4, value: 1, site: 1 },
{ id: 5, value: 3, site: 1 },
{ id: 6, value: 1, site: 1 }
];
// Create the data store
// Store information in a data store on the client side
var store = Observable(new Memory({
data: {
identifier: "id",
label: "Users Online",
items: data
}
}));
var result = new StoreSeries(store, { query: { site: 1 } }, "value");
//function does not get past here (checked using alert())
}
require([
// Require the basic chart class
"dojox/charting/Chart",
// Require the theme of our choosing
"dojox/charting/themes/Tufte",
// Charting plugins:
// We want to plot Pie and ClusteredColumns charts
"dojox/charting/plot2d/Pie",
"dojox/charting/plot2d/ClusteredColumns",
"dojox/charting/plot2d/Columns",
"dojox/charting/plot2d/Grid",
// Retrieve the Legend, Tooltip, and MoveSlice classes
"dojox/charting/action2d/Tooltip",
"dojox/charting/action2d/MoveSlice",
"dojox/charting/action2d/Highlight",
// We want to use Markers
"dojox/charting/plot2d/Markers",
// We'll use default x/y axes
"dojox/charting/axis2d/Default",
"dojo/parser",
"dojo/store/Observable",
"dojo/store/Memory",
"dojox/charting/StoreSeries",
"dijit/dijit", // loads the optimized dijit layer
"dijit/Calendar",
// Wait until the DOM is ready
"dojo/domReady!"
], function(Chart, theme, Pie, ClusteredColumns, Columns, Grid, Tooltip, MoveSlice, Highlight, Observable, Memory, StoreSeries) {
setupWeekElectricBar(Chart, theme, ClusteredColumns, Columns,Tooltip, Highlight, Observable, Memory, StoreSeries);
}
});
Fixed by moving
"dojo/store/Observable",
"dojo/store/Memory",
"dojox/charting/StoreSeries",
further up.

Categories