AmCharts4: getPolygonById method for custom map from GeoJSON? - geojson

I am attempting to use the getPolygonById method with a map polygon series created using custom GeoJSON data (this is in order to zoom to a polygon with a specific ID). I have followed the instructions provided on creating custom maps.
The Map Polygon Series from the custom GeoJSON data renders and events function correctly (eg to zoom to a polygon or color change on "hit").
However, when calling customSeries.getPolygonById("ExampleID"), the method returns "undefined". Note that the GeoJSON source for customSeries includes the corresponding id field (ie "id": "ExampleID").
That is, despite rendering correctly, the following code returns "undefined":
var customSeries = map.series.push(new am4maps.MapPolygonSeries());
customSeries.geodataSource.url = "/geojson/customSeries.geojson";
customSeries.useGeodata = true;
console.log(customSeries.getPolygonById("ExampleID"));
This contrasts with the return of the JSON object with specified Map Polygon when using an Amcharts map template. For example, the following code returns an object corresponding to the Map Polygon with "US" id:
var worldLow = map.series.push(new am4maps.MapPolygonSeries();
worldLow.useGeodata = true;
worldLow.geodata = am4geodata_worldLow;
console.log(worldLow.getPolygonById("US"));
Is this an issue with my data? Or is something additional required to call getPolygonById on a Map Polygon Series from custom GeoJSON?

Loading data through the DataSource / GeoDataSource object is done asynchronously, so your getPolygonById call is executing before the data has been parsed and loaded into the series. You can either use the done event in the DataSource or datavalidated event in the series to check for whether the data has been loaded before calling getPolygonById
polygonSeries.geodataSource.events.on('done', function() {
// timeout needed as the data isn't complately loaded at this point
setTimeout(function() {
console.log('done: ', polygonSeries.getPolygonById('BCH'))
}, 100)
})
// OR
polygonSeries.events.on('datavalidated', function() {
// check if there's data loaded in the array before looking up
// the polygon
if (polygonSeries.data.length) {
console.log('data loaded', polygonSeries.getPolygonById('BCH'))
}
})
Demo

Related

Computed property not updating with mutating function values

I seem to have a very perplexing Swift problem.
I have a struct called "Graph" which has a number of mutating functions that change its stored properties. The struct's initializer reads from a text file and saves the data in various stored properties.
struct Graph {
var dists: [[Int]]
.
.
.
func tourValue() -> Int {
return dists.count
}
mutating func swapHeuristic() {
dists = [[0], [1]]
}
mutating func twoOpt() {
dists = [[1]]
}
.
.
.
}
I also have a function makeFile() which creates a text file for the graph to read from.
The main issues lie with my ContentView.swift file, where I create and use instances of Graph.
ContentView.swift
import SwiftUI
struct ContentView: View {
var nodes: Int
var graph: Graph { // Graph object is initialized and stored perfectly
set {
makeFile(nodes: nodes, selection: .Euclidean) // this works perfectly
}
get {
Graph(flag: -1, filename: "\(nodes)nodesEUCLIDEAN.txt") // this works perfectly
}
}
var originalTour: Double {
graph.tourValue() // saves the original tour value perfectly
}
var swapValue: Double {
set {
graph.swapHeuristic() // does not change Graph's dists property like it should
}
get {
return graph.tourValue() // as a result of dists not changing, the tourValue is also unchanged
}
}
var body: some View {
Text("Hello, World!")
}
}
Thus, the initialized graph instance never has its property changed, and I can't derive values from it.
How do I solve this?
Your graph variable is computed, so unless its mutated form is reflected somewhere else (other objects/properties, a file, etc) the mutation is inevitably lost.
When you set swapValue = 0, first the getter for var graph is called, creating a new Graph instance. Then that instance executes swapHeuristic(). Since that function mutates the value, it triggers the setter for var graph. The serious red flag here is that your setter ignores newValue. newValue in the setter is at that point the only piece of your code which has the mutated form of your graph. But it does nothing with that value, so when the setter finishes it's simply deallocated.
The question is already answered, but in attempt to be helpful, let me come at the same point from a slightly different direction.
The line
var graph: Graph { // Graph object is initialized and stored perfectly
is a form of self delusion. There is no Graph object that is initialized and stored. This is, as you've been told, a computed property. So consider it in full:
var graph: Graph { // Graph object is initialized and stored perfectly
set {
makeFile(nodes: nodes, selection: .Euclidean) // this works perfectly
}
get {
Graph(flag: -1, filename: "\(nodes)nodesEUCLIDEAN.txt") // this works perfectly
}
}
What we see in the get part (the getter) is not the retrieval of a stored Graph. It is the creation of a new graph. Every single time we ask for graph, a completely new and different Graph is created at that moment and passed out to the caller.
Thus there is no existing object to mutate. The line graph.swapHeuristic() causes a completely new Graph to come into existence, calls the method, and throws that Graph away.

Use iteration helpers like .map(), .where(), etc in an async manner?

Short: toList() executes before makeMaker causing the markers to have null objects.
Long: In Firestore, I have table and game collections and inside of table, there is a game field(type=reference). With a StreamBuilder, I can access tables. And I iterate through tables and try to fill in their game fields with real data by using get as seen in below;
if (snapshot.hasData) {
tabledocs = snapshot.data.documents;
markers = tabledocs
.map((tabledoc) {
DocumentReference gameref = tabledoc.data['game'];
//game field is a reference type field which points to another collection
gameref.get().then((gdoc) {
tabledoc.data['game'] = gdoc;
Marker marker = makeMarker(tabledoc); //<--------Executes later
return marker;
});
}).toList(); //<--------Executes first
}
Because gameref.get().then() takes time, the toList() at the bottom executes before each marker is generated and added in to markers.
If there are 3 markers returned from Firestore, our markers is an array of 3 null markers. I think makeMarker(..) did not execute yet most probably.
Is there a way for the map method to wait for the gets to finish and then initialize markers array with non-null values? Or can you show me another way to accomplish what I want.
You can either use
await for(var tabledoc of tabledocs) {
}
or if it is not necessary that the items are executed in order (the result will be in the order of the original items though)
var markers = await Future.wait(tabledocs
.map((tabledoc) {
DocumentReference gameref = tabledoc.data['game'];
//game field is a reference type field which points to another collection
var gdoc = await gameref.get();
tabledoc.data['game'] = gdoc;
Marker marker = makeMarker(tabledoc);
return marker;
});

Creating a layer in javascript?

I have a code that is working with a canvas and I'd like to convert it into a layer.
The problem is that I do not want to use the build mechanism of OL3, I just want to use plain javascript.
At the moment, the problem I have is that my handleRender_ function is never called.
Here is my JS code :
ol.layer.MyLayerProperty = {
};
ol.layer.My = function (opt_options) {
var options = opt_options || {};
ol.layer.Layer.call(this, options);
this.on('render', this.handleRender_.bind(this)); //I suspect this is not working
};
ol.inherits(ol.layer.My, ol.layer.Layer);
ol.layer.My.prototype.handleRender_ = function (event) {
console.log('render process'); //never called
};
In fact, to display a canvas "above" openlayers, you simply have to use ImageCanvas.
see http://www.acuriousanimal.com/thebookofopenlayers3/chapter03_04_imagecanvas.html for example

How to perform an action on all CEMarker's in a CEMarkerGroup

I've setup CEMarkerGroup's according to my data, and have successfully displayed them. According to Citymaps' documentation, they indicate the following:
USING A MARKER GROUP
Marker groups allow you to organize your markers and perform functions on all markers in the group simultaneously, and also perform certain operations which you would need to implement yourself otherwise.
However, there don't appear to be any exposed class or instance methods that allow action on a particular group. Below, I've setup code
CEMarkerGroup *grpCondo = [self.mapView markerGroupWithName:#"grpCondo"];
CEMarkerGroup *grpRental = [self.mapView markerGroupWithName:#"grpRental"];
CEMarkerGroup *grpCoOp = [self.mapView markerGroupWithName:#"grpCoOp"];
CEMarkerGroup *grpCondop = [self.mapView markerGroupWithName:#"grpCondop"];
Later, as I loop through the list of markers I'm adding, I specify the group based on a category (cat) value.
if ([cat isEqualToString:#"Condo"]) {
[grpCondo addMarker:marker];
}
if ([cat isEqualToString:#"Condop"]) {
[grpCondop addMarker:marker];
}
if ([cat isEqualToString:#"Rental Unit"]) {
[grpRental addMarker:marker];
}
if ([cat isEqualToString:#"Co-op"]) {
[grpCoOp addMarker:marker];
}
These groups, already associated with my map object, display fine, but I cannot find any way to act on these individual groups (e.g., hide a group, show a group, etc.)
Any thoughts out there?
Thanks!
I am a developer at Citymaps.
CEMarkerGroup is rather bare, and for the most part just a way to organize where your objects are.
The only batch operation we have a on a marker group right now is to remove all markers in that group from the map. We also have the collision detection feature, which I saw your other post on.
CEMarkerGroup does provide readonly access to its markers, if you want to perform some action to each CEMarker in the group.
EDIT: To answer your comment, here is a code sample of how to toggle markers in a marker group.
// This would be your toggled value
BOOL showRentals = YES;
for(CEMarker *rentalMarker in grpRental.markers) {
// This property is not yet exposed. This would have the marker automatically fade in or out based on fadeTime.
//rentalMarker.hidden = !showRentals;
// You can use this as a proof of concept
rentalMarker.alpha = showRentals ? 1.f : 0.f;
}

Retrieve the content of series data [] in Highcharts, after rendering the chart?

I would like to add in the click event, a line of code that when I click on the chart, grab the content of the data [] in its series, to save it in a variable for future use.
Which one is the syntax to do so? this.chart.series didn't work.
I am planning to pass this to another chart, as data:
This is what I have so far; tried with get() also but I still get undefined errors
chart: {
renderTo: 'chart',
events: {
click:function(event){
extracteddata=this.chart.get('mainseries');
}
},
}
When I print out in console; the only thing that I get is "this"; which return me the whole chart object including everything inside.
Tried so far with
this.series.option.data
this.data
this.series
And neither return the content of the array. I have printed out "this" and I can clearly see the series.data array which is correctly populated.
this.series is an array of series objects, so assuming you want the first series, use:
events: {
click: function (event) {
var someData = this.series[0].data;
}
}
someData here is an array of point objects. If you want just numbers use
this.series[0].yData
and/or
this.series[0].xData

Resources