Angular google maps dynamic polyline with a lot of points - angularjs

I have a polyline which can contain a lot of points, and also can be changed according to the user behavior, I am currently using Angular Google maps 1.2.x and employment strategy is as follows:
myService.getPoints(id)
.success(function(data){
//in other controller have $scope.p = pService
pService.Polyline = [];
for(var i=0;i<data.length;i++){
var p = {
latitde : data.Latitudefield,
longitude : data.Longitudefield
}
pService.Polyline.push(p);
}
})
This works, but when the number of points is a lot (about 4000 points or more), the application becomes very slow, what strategy can be used to handle this?

This is how I did it to get a faster rendering.
$scope.trackings = [{
id: 1,
geotracks: [{
latitude: 23.0,
longitude: 72.58
}, {
latitude: 23.1,
longitude: 72.58
}]
}, {
id: 2,
geotracks: [{
latitude: 24.0,
longitude: 72.58
}, {
latitude: 23.1,
longitude: 71.58
}]
}];
<div class="row" ng-if="map.show">
<ui-gmap-google-map center="map.center" zoom="map.zoom">
<ui-gmap-polylines models="trackings" path="'geotracks'" static="true"></ui-gmap-polylines>
</ui-gmap-google-map>
</div>
The key here is static="true". I had to wrap it all in a ng-if="map.show" that I set true when data is ready, because the static keyword makes it, well, static (and not async). Hope this helps :)
Ref: http://angular-ui.github.io/angular-google-maps/#!/api/polylines
static: Boolean property which indicates that the Polyline coordinates are static.(I.e. will not change).
Declaring a static path will improve the performance of the directive as it does not need to monitor every coordinate in the path for changes during the Angular $digest cycle.
Notes:
Declaring a static path implies that it is not editable. As a result, the editable parameter will be ignored.
For static paths, the path attribute will watched by reference so the path may be changed in its entirety and the map will update.
Changes to the static declaration are ignored after the directive is instantiated. (NOT WATCHED)

Related

How to animate dynamic updates in CanvasJS using React functional components?

Using React functional components, I have not been able to find a way to animate my chart with dynamic data received asynchronously. The sandbox below illustrates the problem with a timer simulating the asynchronous read.
https://codesandbox.io/s/basic-column-chart-in-react-canvasjs-0gfv6?fontsize=14&hidenavigation=1&theme=dark
When running the example code, you should see 5 vertical bars of increasing heights animate. Then, after 5 seconds, it switches immediately to 4 bars of descending heights. I am looking to have that update animate.
Here is some reference information I've reviewed:
CanvasJS React Demos: many of which animate on initial draw, but I couldn't find one that animates with dynamic data loaded after the initial render.
Chart using JSON Data is an demo that has dynamic data, but doesn't animate.
Reviewing the CanvasJS forum, I found a couple links, but none that address React functional components
Vishwas from Team Canvas said:
To update dataPoints dynamically and to animate chart, you can instantiate the chart, update dataPoints via chart-options and then call chart.render as shown in this updated JSFiddle.
var chart = new CanvasJS.Chart("chartContainer", {
title: {
text: "Animation test"
},
animationEnabled: true,
data: [{
type: "column",
dataPoints: []
}]
});
chart.options.data[0].dataPoints = [{ label: "Apple", y: 658 },
{ label: "Orange", y: 200 },
{ label: "Banana", y: 900 }];
chart.render();
This sample is pure JS, but I tried to adapt the principle to my React functional component. To better comport with React best practices, I incorporated the useState hook for storing the data and the useEffect hook to handle the fetch. But, alas, I couldn't get my sandbox to animate with the dynamic data.
I think the problem is that CanvasJS expects to animate only on the first render, as stated by Sanjoy in the CanvasJS forum on 7/19/2016.
I found this SO question from Jan 2015 that suggests:
My current ugly workaround is to reinstantiate the chart every time I
update just to achieve that animation effect.
I'm hopeful that the situation has improved in the last four years, but if this hack is still the best/only way to go, I need some guidance on how to reinstantiate the chart every time using a React functional component.
To force a remount of a component pass a different key when you want to remount the component
<CanvasJSChart
key={dataPoints.toString()} // force a remount when `dataPoints` change
containerProps={containerProps}
options={options}
onRef={ref => (chart.current = ref)}
/>
Working example
I found a partial answer. Full executing code is in this code sandbox, but the critical bit is to delay the initial render of the chart until a state variable indicates that the data is available:
return (
<div className="App">
{!initialized ? (
<h1> Loading...</h1>
) : (
<CanvasJSChart containerProps={containerProps} options={options} />
)}
</div>
);
This is only a partial solution because subsequent data updates still do not animate.
Both examples works fine.
You can always animate chars with some kind of calling. I use in this case setInterval.
<script src="https://canvasjs.com/assets/script/canvasjs.min.js"></script>
<script>
var chart;
window.onload = function () {
chart = new CanvasJS.Chart("chartContainer", {
title: {
text: "Animation test"
},
animationEnabled: true,
data: [{
type: "column",
dataPoints: []
}]
});
chart.options.data[0].dataPoints = [{ label: "Apple", y: 0 },
{ label: "Orange", y: 0 },
{ label: "Banana", y: 0 }];
chart.render();
}
var max = 0;
var s = {c: 0, i: 0};
function ANIMATE() {
if (typeof chart === 'undefined') return;
chart.options.data[0].dataPoints.forEach(function(item, index, array) {
if (index == s.i) {
array[index].y += 3;
s.c++;
}
if (s.c > 12) {
s.i++;
s.c = 0;
if (s.i == 15) { s.i = 0}
}
});
if (max < 12) {
chart.options.data[0].dataPoints.push({label: "apple" + Math.random(), y: 1 + Math.random() * 10});
max++;
}
chart.render()
}
setInterval(function(){
ANIMATE()
}, 1)
</script>
<div id="chartContainer" style="height: 370px; width: 100%;"></div>

angular scope object issue

PROBLEM:
I created one var object and inside that var object i am referring
to slider scope object. so when I will use my slider it will update
scope object but not update in reference object. i.e
filterObject.filter.priceRange.min
so for changin var object i have to do it manually then it will work
you can see my fiddle so you will understand what i am trying to say because i don't know how to explain my problem.
Workinh example:
http://jsfiddle.net/kevalbhatt18/wk6xhy3k/4/
see what i did:
// this object is for slider
$scope.priceRangeSlider = {
minValue: 100,
maxValue: 10000,
options: {
floor: 100,
ceil: 10000,
step: 1,
translate: function(value) {
return 'Rs ' + value;
}
}
};
//var object refere to scope object
var filterObject = {
filter: {
priceRange: {
min: $scope.priceRangeSlider.minValue // refe not working
},
yearRange: {},
languageValue: [],
formatValue: []
}
};
HOW YOU DEBUG:
slide "price slider" so you can se changeing value below the yearslider, after changing value of price slider click on button below the price slider you will get 100 which is allocated at onload of application
The reason to the problem is that filterObject.filter.priceRange.min will be evaluated only once, when the controller is run for the first time.
One fix is to change it to a function:
var filterObject = {
filter: {
priceRange: {
min:function () { return $scope.priceRangeSlider.minValue; }
...
Then:
$scope.minvaluetest = filterObject.filter.priceRange.min();
Fiddle: http://jsfiddle.net/masa671/9kjqeueo/
UPDATE:
You just need to turn the logic the other way around.
See a new Fiddle: http://jsfiddle.net/masa671/z5fkvh5b/
Now filterObject has the value that can be sent to a server.

Why panning google map updates angularjs scope var?

Kind of an odd question/hard to find an answer. I'm relatively new to using angularjs and have a problem i'm confused about.
I've set up a google map using http://angular-ui.github.io/angular-google-maps/#!/ and have an issue.
So i have a $scope.homeBase where i am setting a lat & long for the owners 'Home Base', i want to use that location to set the on load map center, & a pin to show the location. So in my mind cool, set the one var and use it in both locations. Wrong.
So this works on load, but every time i pan the map, i guess it re-sets the map center location, which in turn binds to the map marker and sends the marker to the middle of the screen, on every pan.
Is there anyway to have the two not bound? or have separate vars? without them bound?
i thought a simple 'var homebase' would remove binding and not update the var. instead of using scope but it still binds?
This perhaps is a newb question but i appreciate any pointing in proper direction.
Thanks again
here's my code.
// uiGmapGoogleMapApi is a promise, The "then" callback function provides a maps object.
uiGmapGoogleMapApi.then(function() {
// Setting homebase coords, these will come from firebase
$scope.homeBase = { latitude: -34.18405, longitude: 150.71319 };
// Set default map.
$scope.map = {
center: $scope.homeBase,
pan: true,
zoom: 14,
events: {
click: function(){
}
}
};
// Set homebase marker.
$scope.homebaseMarker = {
id: 0,
coords: $scope.homeBase,
options: { draggable: false },
};
});
I was able to set the center on page load via
angular.copy(var)

custom shape for markers using angular google maps

I am trying to use the angular google maps(http://angular-ui.github.io/angular-google-maps/) directive in my application, so converting my code to work with that directive and stuck up in that process to apply custom shape to the markers.
Anybody who is familiar with angular google maps can help me to resolve this issue, please check the below mentioned code written in plain google maps library and let me know how to make this code working with the angular google maps directive.
new google.maps.Marker({
position: latLng,
map: map,
icon: {
url: 'path/to/icon',
origin:new google.maps.Point(0,0),
anchor:new google.maps.Point(3,4)
},
shape: {
coords:[0, 6, 7, 0, 16, 0, 23, 6, 12, 12],
type:"poly"
}
});
Yeah, I found the solution here, we need to set that as an attribute to OPTIONS object as mentioned below:
markers.push({
latitude: site.latitude,
longitude: site.longitude,
id: 1,
icon: {
url: 'path/to/icon',
origin: origin,
anchor: anchor
},
options: {
shape: {
coords: phaseMarker.coords,
type: "poly"
}
}
});
Note: I tried this even before posting this question, but that didn't worked well due to duplicate ID attribute for markers, so make sure we have unique ID for each marker if we have a set of markers in our map.

Marker not updating coordinates in Google Maps for AngularJS

I am using Google Maps for AngularJS and have the following Jade code:
#map_canvas
google-map(center='map.center', zoom='map.zoom', draggable='true', options='options', events='map.events')
marker(coords='marker.coords', options='marker.options', idkey='marker.id')
In my angular controller I have:
$scope.map = {center: {latitude: 42.2405, longitude: -8.7207 }, zoom: 12, events: {
click: function (map, eventName, args) {
$scope.marker.coords.latitude = args[0].latLng.lat();
$scope.marker.coords.longitude = args[0].latLng.lng();
console.log($scope.marker);
}
}
}
$scope.marker = {
id: 0,
coords: {
latitude: 42.2405, longitude: -8.7207
},
options: { draggable: true }
}
What I am trying is to update the marker location with every click.
At console.log($scope.marker); I can see that my marker does print the updated coordinates value but the red pin does not move on the map.
I can't figure what I am doing wrong.
EDIT:
The marker does move to the new location after I resize the map, so I figured it is a matter of the map no refreshing when marker location changes. Should this be reported as a bug? Anything I can do to solve it?
Finally! After reading a bit on $apply() here and here and finding solutions like this one where it mentions to:
wrap your callback body into $scope.$apply(function () { ... });. Remember
that your event is coming from "non-Angular" world.
I found this other post where it just adds $scope.$apply(); to the end of the click function.
This last solution was the only one working for me.
I've posted all the process cause I consider it interesting to learn about $apply().

Resources