Leaflet JS Visualization through static API response using JQuery - javascript

I'm getting a static response from an API (it returns the same data on every call) and based on that response I'm changing visualization properties of a GeoJSON object.
Currently my workflow is:
Read GeoJSON in frontend
Hit API to get its response
Loop through the api keys and match them with GeoJSON's key. Symbolize based on value when ID gets matched.
The issue with this approach is that on every event, I'm making a new request and getting the same data. Also, after getting the data, the loop takes lots of time to complete and then the visualization property gets applied.
I have tried storing API response in a variable in the frontend code as well but the time taken while looping through the ID's is still a lot. I can't think of any ways to store the visualization properties somewhere to make the symbology change of GeoJSON rapidly.
Some code example to implement this would be helpful. My current code is shared below:
$(".ambtn").on("click",function(){
$.ajax({
method:"get",
url:"/los_am",
beforeSend: function(){
$("#loader").css({
display: "inline-block",
visibility: "visible"
});
},
complete: function(){
$("#loader").css({
display: "none",
visibility: "hidden"
});
},
success:function(res){
// console.log(res)
rdNetworkLyr.eachLayer(function (layer) {
for (const key in res.XDSegID){
if(res.XDSegID.hasOwnProperty(key)){
if(layer.feature.properties.XDSegID === res.XDSegID[key] && res.los[key] === 'A'){
layer.setStyle({color:'#060'})
}
else if (layer.feature.properties.XDSegID === res.XDSegID[key] && res.los[key] === 'B'){
layer.setStyle({color:'#9f0'})
}
else if (layer.feature.properties.XDSegID === res.XDSegID[key] && res.los[key] === 'C'){
layer.setStyle({color:'#ff3'})
}
else if (layer.feature.properties.XDSegID === res.XDSegID[key] && res.los[key] === 'D'){
layer.setStyle({color:'#f90'})
}
else if (layer.feature.properties.XDSegID === res.XDSegID[key] && res.los[key] === 'E'){
layer.setStyle({color:'#f60'})
}
else if (layer.feature.properties.XDSegID === res.XDSegID[key] && res.los[key] === 'F'){
layer.setStyle({color:'#c00'})
}
}
}
});
}
});
});
My aim is to pass that stored symbology to "setStyle" function without having to loop every time the request is made.

the loop takes lots of time to complete
the time taken while looping through the ID's is still a lot
Unless you have millions of keys to loop through, the algo part in itself is rarely the bottleneck.
What may slow down things is the style change of individual layers in your GeoJSON Layer Group rdNetworkLyr: if that makes the browser recompute and repaint the entire Group at every individual change, for sure it will take a lot of work at every step, especially if your group is big!
You can try a simple workaround by removing your rdNetworkLyr Layer Group from the map first before applying all the individual style changes, then adding it back:
function (res) {
rdNetworkLyr.remove();
// Change all the styles
rdNetworkLyr.addTo(map);
}

Related

How to create a conditional based on a response object, for which the Key values are dependent and avoiding object.key() is Undefined

I'm subscribing to a method in Angular using typescript, which is a simple post Call that returns an entire HTTP Response. I'm wanting to create a simple conditional that checks to see if a certain key exists in the HTTP response. The conditional will work with an object that contains the key, but for an object that doesn't contain the key it throws an undefined.
Is there a way to omit the error or exception thrown and to proceed to the else portion
The keys of the object body are added if the user includes a number, without that it doesn't exist with the PhoneNumber key value
what I've tried / flipped the if and else
!= undefined (vice versa)
== null (vice versa)
this.postMyName(userInfo).subscribe(
(result:Response) => {
if (result['body']['user'].hasOwnProperty(['PhoneNumber'])) {
console.log("User has included a phoneNumber");
} else {
console.log("User has Not included a phoneNumber");
}
},error ...)
You can also try this solution maybe it will work
this.postMyName(userInfo).subscribe(
(result:Response) => {
if ('PhoneNumber' in result['body']['user'])) {
console.log("User has included a phoneNumber");
} else {
console.log("User has Not included a phoneNumber");
}
},error ...)

uncaught error data undefined on ajax call

I'm making an ajax call (JavaScript) which generates through the show info function the data I'm retrieving. My problem is as follows: On one of the arrays some data don't have it, there are not many but there is still some. So it's displaying the first items but it stops when it can't retrieve the said array and return a:
Uncaught TypeError: info["xxxx"] is undefined.
What I'd really like is to be able to make it so it still retrieve / display the datas and says something like 'this data.[denomination][0].title is undefined or anything else'.
I tried to use the optional chaining operator '?.' but I clearly have no idea on how it works.
Here's what makes me get crazy: (it's the data["denomination"] that ruins it all)
request.addEventListener('readystatechange', function(){
if (request.readyState === XMLHttpRequest.DONE && request.status === 200) {
const backCall=JSON.parse(request.responseText);
if(backCall.count != 0){
for(let data of backCall.datas){
showInfo(data.title, data["author"][0].name, data["denomination"][0].title, data["denomination"][0].id);
}
}else if(backCall.count === 0){
noResult();
}
}
});
(just a little edit to be precise. I searched before hand and even looked up to the advised subjects from Stack when I was writing this)
Check if both properties exist in the object and then call your showinfo function, it should not fail
for (let data of backCall.datas) {
if (data["author"] && data["denomination"]) {
showInfo(data.title, data["author"][0].name, data["denomination"][0].title, data["denomination"][0].id);
}
}

If statement with logical or operator

I want my function to check to see if the status is 'closed' or 'complete' and if it is either of those to perform the action in brackets.
For some reason I can't seem to make that work, it will check closed but not check complete.
for (var id in games) {
if (games[id].league === 'NCAAB')
if ((games[id].status == 'complete')||(games[id].status == 'closed')) {
sports.clearTimer();
}
if (ts >= games[id].scheduledTimeUnix) { xxxxxx}
Right now the timer isn't being cleared despite the fact the only two status for the nodes are complete and closed. The XXXX represents api calls that occur and the function is constantly making those calls.
or check for the true, if it gets one true it will not check other condition and perform the action preceding it.
if ((games[id].status == 'closed')||(games[id].status == 'complete'))
so if you get
games[id].status == 'closed'
as true , it will not check for
(games[id].status == 'complete')

Dynamoose/DynamoDB update saving empty array as null

I'm using the Node.js package Dynamoose to handle DynamoDB requests in my web application.
Problem is when I try to update the item whenever the JSON object includes an empty array Dynamoose seems to set that to null. Or it could be DynamoDB for all I know.
Below is part of my schema that I'm using for this table.
var NoteSchema = new dynamoose.Schema({
_id: String,
details: Array
});
In the code below the variable body is set to {details: []}. I have confirmed this by running console.log(body);.
Note.update({
_id: searchid
}, body, function(err, note) {
if (err) {
console.log(err);
} else {
console.log(note);
}
});
Problem is inside that callback function when running console.log(note); details doesn't even show up at all. So it's null or undefined. In the Amazon Web Services again details doesn't exist at all for that entry.
What is so strange is when creating a new Note, setting details = [], and saving that, details is an empty array and works perfectly. So to me it seems like a specific problem with updating the record and setting that property to an empty array, since creating a record and setting that property to an empty array works perfectly.
How can I update the record and set details to an empty array?
Figured this out. Submitted this issue to the GitHub repo. Basically the following line (lib/Model.js about line 396) of code checks to see if the value is an array with length = 0. If so it will delete that JSON key before sending it to AWS.
if(val === null || val === undefined || val === '' || (Array.isArray(val) && val.length === 0)) {
I submitted a pull request using my fork. In that pull request I made the change to allow an optional JSON object parameter of options to be passed into the update function. This is the 3rd parameter of the update function and right before the callback function. If there is a key called emptyarrayallowed and that value is set to true then you that line above will get changed into the following.
if(val === null || val === undefined || val === '') {
For example in my example above changing the update function to the following will work (as long as you are using my fork or the pull request has been approved).
Note.update({_id: searchid}, body, {"emptyarrayallowed": true}, function(err, note) {
if (err) {
console.log(err);
} else {
console.log(note);
}
});
Let me know if you have any questions regarding this solution.
Small update on Charlie's answer. The functionality was merged as mentioned by Charlie. However, looks like the property name was renamed to allowEmptyArray from emptyarrayallowed.

Angular Asynchronous Timing

I am working on a updating list that shows twitch information about certain users. It should show their logo a, name, and if they are online. Currently, I am grabbing the channels and putting them into three arrays. One array for the Twitch channel information that contains information on all channels. One array that holds the display_name of the online users. One array that holds the display_name of the offline users.
It currently works, but it only works if you press the beginning button. Why is the button necessary and how do I make the list populate without the button?
http://codepen.io/crosscris/pen/ZGEOor?editors=101
var app = angular.module('FCCTwitchChecker', []);
app.controller('FCCTwitchController', function() {
var TwitchCheck = this;
var FCCstreamers = ["freecodecamp", "storbeck", "terakilobyte", "habathcx","notmichaelmcdonald","RobotCaleb", "medrybw","comster404","brunofin","thomasballinger","joe_at_underflow","noobs2ninjas","mdwasp","beohoff","xenocomagain"];
TwitchCheck.AllUsersChannelObjects = [];
TwitchCheck.OfflineUsers = [];
TwitchCheck.OnlineUsers = [];
var BeginningURL= "https://api.twitch.tv/kraken/";
FCCstreamers.forEach(function(streamer){
var streamURLstring = BeginningURL + "streams/" + streamer + "?client_id=1234chrisclientid4321&callback=?";
var channelURLstring = BeginningURL +"channels/" + streamer + "?client_id=1234chrisclientid4321&callback=?";
$.ajax({
type: "GET",
dataType: "json",
url: streamURLstring,
success: function(result) {
if(result.stream === null || result.error==="Not Found") {
TwitchCheck.OfflineUsers.push(streamer);
} else {
TwitchCheck.OnlineUsers.push(streamer);
}
},
error: function() {
console.log("It failed");
}
});
$.ajax({
type: "GET",
dataType: "json",
url: channelURLstring,
success: function(result) {
if (result.error !== "Not Found") {
TwitchCheck.AllUsersChannelObjects.push(result);
if (result.logo === null) {
TwitchCheck.AllUsersChannelObjects[TwitchCheck.AllUsersChannelObjects.length - 1].logo === "http://placehold.it/350x150";
}
}
},
error: function() {
console.log("It failed");
}
});
});
TwitchCheck.showArray = function () {
console.log(TwitchCheck.AllUsersChannelObjects);
}
TwitchCheck.OnorOff = function(ChannelName) {
if(TwitchCheck.OnlineUsers.indexOf(ChannelName.name) !== -1) {
return "on";
} else {
return "off";
}
}
});
I was given a list of display_names and some of the display names are not valid channels. I believe ajax/angular is stuck trying to find information of invalid channels, but I am not sure.
Since the jQuery http call is outside of Angular digest cycle (it is an asynchronous call), you need to manually run that digest cycle using $scope.$apply() from your controller in the success callback.
See example here: http://codepen.io/anon/pen/oXgqGM
However, as suggested by mcranston18 on a comment, it is better to use Angular's $http service rather than jQuery, as it takes care of the scope digestion and you don't need to do it yourself.
This is because Angular updates all of its views, based on diffs, during its $digest cycle.
It uses a cycle of checking all of its children and its chilren's children's children, etc... so that it only does the work of checking when it knows something has changed.
...but the only way it knows something has changed, automagically, is if that change is because of an event in the Angular system.
Now, these changes might be so magical and so well hidden that you've never noticed or thought about them.
That's totally fine. We've all been in that place, and it means that the ng-team has done a great job of hiding those complexities, to make the experience seamless.
But reaching outside of angular to grab data or modify data, and then reaching into Angular's world from outside to poke at Angular values isn't something that Angular knows how to check against.
The reason it works when you press the button is because it causes a hidden event, which tells Angular: "After this event has run (and all of its subroutines run), do a digest and apply all changes based on the diffs you find."
That (or similar) is the glue which keeps Angular (and React and ...library-X) magical.
To keep Angular happy, use $http

Categories

Resources