Data from Erddap to Leaflet Velocity map - javascript

My organisation uses the Erddap data server to publish oceanographic models. We also use Leaflet to publish web maps and are looking at using the Leaflet Velocity library to create visualisations of modelled sea surface velocity.
The code below creates something that looks like the (not well documented) JSON format expected by Leaflet Velocity, and it seems to parse as I get no errors in my JavaScript Console - but nothing is displaying on the map.
Anyone have any ideas?
const minLat = 48.5;
const maxLat = 58.5;
const minLon = -17.0;
const maxLon = -2.0;
const refTime = '2019-12-15T00:00:00Z';
const spacingLat = 0.025;
const spacingLon = 0.025;
const strideLon = 20;
const strideLat = 20;
const strideTime = 1;
var mymap = L.map('map').setView([maxLat-((maxLat-minLat)/2), maxLon-((maxLon-minLon)/2)], 7);
var gebco = L.tileLayer.wms('https://www.gebco.net/data_and_products/gebco_web_services/2019/mapserv?',{layers: 'gebco_2019_grid'}).addTo(mymap);
fetch('https://erddap.marine.ie/erddap/griddap/IMI_Model_Stats.json?sea_surface_x_velocity[(' + refTime + '):1:(' + refTime + ')][(' + String(minLat) + '):' + String(strideLat) + ':(' + String(maxLat) + ')][(' + String(minLon) + '):'+ String(strideLon) +':(' + String(maxLon) + ')],sea_surface_y_velocity[(' + refTime + '):1:(' + refTime + ')][(' + String(minLat) + '):' + String(strideLat)+ ':(' + String(maxLat) + ')][(' + String(minLon) + '):' + String(strideLon) + ':(' + String(maxLon) + ')]').
then(
response => response.json()
).
then(
data => [{
'header':{
'la1': maxLat,
'la2': minLat,
'lo1': minLon,
'lo2': maxLon,
'dx': strideLon * spacingLon,
'dy': strideLat * spacingLat,
'nx': [...new Set(data.table.rows.map(x => x[2]))].length,
'ny': [...new Set(data.table.rows.map(x => x[1]))].length,
'parameterCategory': 2,
'parameterNumber': 2,
'parameterUnit': 'm.s-1',
'parameterNumberName': data.table.columnNames[3],
'refTime': refTime.replace('T', ' ').replace('Z','')
},
'data': data.table.rows.map(x => x[3])
},{
'header':{
'la1': maxLat,
'la2': minLat,
'lo1': minLon,
'lo2': maxLon,
'dx': strideLon * spacingLon,
'dy': strideLat * spacingLat,
'nx': [...new Set(data.table.rows.map(x => x[2]))].length,
'ny': [...new Set(data.table.rows.map(x => x[1]))].length,
'parameterCategory': 2,
'parameterNumber': 3,
'parameterUnit': 'm.s-1',
'parameterNumberName': data.table.columnNames[4],
'refTime': refTime.replace('T', ' ').replace('Z','')
},
'data': data.table.rows.map(x => x[4])
}]
).then(
report => {
//document.getElementById('dump').innerHTML = JSON.stringify(report);
try{
var velocityLayer = L.velocityLayer({
displayValues: true,
displayOptions: {
velocityType: "GBR Water",
displayPosition: "bottomleft",
displayEmptyString: "No water data"
},
data: report,
maxVelocity: 0.3,
minVelocity: 0,
velocityScale: 0.1,
opacity: 0.97
}).addTo(mymap);
document.getElementById('dump').innerHTML = JSON.stringify(report);
console.log(report);
} catch(err) {
document.getElementById('err').innerHTML = err.message;
}
}
)

In the end, I sorted this out and am leaving the link here to a working example of the code
https://irishmarineinstitute.github.io/erddap-leaflet-velocity-demo/
https://github.com/IrishMarineInstitute/erddap-leaflet-velocity-demo

Related

Reactjs Show Real-Time Data from Database in Popup Window

I have a GUI that shows live data of little icons moving around based on the data from my postgresql database. When I click on one of the icons, a popup window shows with the data from the database. However, it only shows data on the time the icon was clicked and I want the data displayed to update as the database updates. I could really use some help.
The Popup Window Event
addClickEvent(id, source, publish) {
this.map.on('click', id, function (e) {
let description = ''
let trackId = e.features[0].properties.id
let trackSource = e.features[0].properties.track_source
let trackIdWithSource = trackId + '_' + source
//HELPFUL TIP: This will need to be updated when you bring over an update to base with the previous version
for (const [key, value] of Object.entries(e.features[0].properties)) {
if (isLowerCase(key)) {
}
else { description += key + ': ' + value + '\n' }
}
let trackWindow = document.getElementById("a" + trackIdWithSource);
if (!trackWindow) {
let trackDescription = document.createElement('div')
trackDescription.id = "a" + trackIdWithSource
trackDescription.innerHTML = description;
trackDescription.style.whiteSpace = "pre-wrap"
document.getElementById('root').appendChild(trackDescription)
new WindowModal({ size: { x: 'max-content', y: 200 }, pos: { x: 300, y: 25 }, title: trackIdWithSource + " Info: ", elementSelector: '#a' + trackIdWithSource })
}
trackWindow = document.getElementById("a" + trackIdWithSource);
//let trackContent = trackWindow.getElementsByClassName("WindowModal-content")
if (publish) {
if (!document.getElementById('publish_' + trackIdWithSource)) {
let publishButton = document.createElement('button')
publishButton.innerHTML = "Publish"
publishButton.id = "publish_" + trackIdWithSource
publishButton.onclick = function () {
let url = process.env.REACT_APP_PROTOCOL + '://' + process.env.REACT_APP_PUBLISH_API_HOST + ':' + process.env.REACT_APP_PUBLISH_API_PORT + '/track/' + trackId + '&' + trackSource + '/publish'
fetch(url, { method: 'post' })
.then(function (data) {
return console.log(data.status);
})
}
publishButton.className = "button"
trackWindow.insertBefore(publishButton, trackWindow.children[1])
}
}
});
const popup = new maplibregl.Popup({
closeButton: false,
closeOnClick: false
});
this.map.on("mouseenter", id, function (e) {
const coordinates = e.features[0].geometry.coordinates.slice();
let description = e.features[0].properties.id;
while (Math.abs(e.lngLat.lng - coordinates[0]) > 180) {
coordinates[0] += e.lngLat.lng > coordinates[0] ? 360 : -360;
}
popup.setLngLat(coordinates)
.setHTML(description)
.addTo(this);
});
this.map.on('mouseleave', id, () => {
this.map.getCanvas().style.cursor = '';
popup.remove();
});
}

How do I get multiple elevations using google api?

I'm trying to get multiple elevations using Google API, but it usually it does not work.
Here is my code:
var axios = require('axios');
var fs = require('fs');
const util = require('util');
var config = {
method: 'get',
url: 'https://maps.googleapis.com/maps/api/elevation/json?key=<MY_API_KEY>&locations=',
headers: { }
};
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function getElevationsWithAPI(config) {
console.log('in getElevationsWithAPI()');
console.log(config);
console.log('method = ' + config.method);
let queryResult = [];
let res = await axios(config); // comment 1
console.log('status = ' + res.status);
console.log('location = ' + res.data.results[0].location.lat + ', ' + res.data.results[0].location.lng);
console.log('elevation = ' + res.data.results[0].elevation);
let results = res.data.results;
for (i = 0; i < results.length; i++) {
let entry = results[i];
queryResult.push({[entry.location.lat + ',' + entry.location.lng]: entry.elevation});
}
const json = JSON.stringify(queryResult);
//console.log('json = ' + json);
return queryResult;
}
function getDistance(lat1, lng1, lat2, lng2) {
var R = 6371; // Radius of the earth in km
var dLat = deg2rad(lat2-lat1); // deg2rad below
var dLon = deg2rad(lng2-lng1);
var a =
Math.sin(dLat/2) * Math.sin(dLat/2) +
Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) *
Math.sin(dLon/2) * Math.sin(dLon/2) ;
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
var d = R * c; // Distance in km
return d;
}
function deg2rad(deg) {
return deg * (Math.PI/180);
}
async function getElevationsExported(fileName, lat1, lng1, lat2, lng2) {
console.log('in getElevationsExported(), fileName = ' + fileName);
console.log(`lat1 = ${lat1}, lat2 = ${lat2}, lng1 = ${lng1}, lng2 = ${lng2}`);
console.log('lat distance: ' + getDistance(lat1, lng1, lat2, lng1));
console.log('lng distance: ' + getDistance(lat1, lng1, lat1, lng2));
console.log('point distance: ' + getDistance(lat1, lng1, lat2, lng2));
var url = config.url;
var res = [];
let counter = 0;
console.log('before for');
try {
// Calculate number of rows
for (i = lat1; i <= lat2 + 0.00026; i += 0.00026) {
counter++;
}
console.log('counter = ' + counter);
// Send the location data to google elevation API
let k = 1;
for (i = lat1; i <= lat2 + 0.00026; i += 0.00026) {
console.log(`${k++}: i = ${i}`);
var locations = '';
let jArr = [];
for (j = lng1; j <= lng2; j += 0.00032) {
jArr.push(j);
locations += i.toFixed(5) + '%2C' + j.toFixed(5) + '%7C';
}
locations = locations.slice(0, -3); // remove the last %7C character
config.url = url + locations;
//console.log('config.url = ' + config.url);
try {
await sleep(10); // comment 2
var elevationRes = getElevationsWithAPI(config);
elevationRes.then(function(result) {
//console.log(result);
counter--;
res.push(...result);
console.log('after --, counter = ' + counter);
if (counter === 0) { // comment 3
console.log('finished getting data');
// Write the new elevations file
fs.writeFile(consts.ElevationDirFullPath + fileName, JSON.stringify(res), (err) => {
if (err) {
console.log(err);
throw err;
}
console.log('The file has been saved');
});
}
});
} catch(error) {
console.log(error);
throw error;
}
}
console.log('exited for loop');
} catch (error2) {
console.log('error2 = ' + error2);
}
}
module.exports.getDistance = getDistance;
module.exports.getElevationsExported = getElevationsExported;
The problem is when I call Google API through axios (see comment 1). Sometimes it succeeds and sometimes it fails. I also tried to add timeout between calls (see comment 2), but then not all calls to getElevationsWithAPI() are called.
Also I usually don't reach the end condition (comment 3).
How can I fix all these problems?

How to replace the data type "undefined" with a text (string)? (weather API project)

I am creating a project that displays the current weather of an area by name search. I fetch the API data from open weather: https://openweathermap.org/current?fbclid=IwAR1SVc9zn9uXaZWLmJA9lYEeZvUc1s_kR68RFadWuIwd8yBjIyJ7zsVMKkE
I have added all the parametres of the API to my code, but if you search some areas, they don't "return" all the weather API parameters. For example, when I search my city's weather at the moment, the parameter " Atmospheric pressure on the sea level" is displaying "undefined" because as Open Weather stated:
Because of that, I don't want the final project to display:
Atmospheric pressure on the sea level: undefined
I want it to return something among the lines of:
Atmospheric pressure on the sea level: not found
As you can see, the data type undefined is replaced with a string.
However, I have found a function here on this site that replaces undefined with "0" and it seems to work because it is marked as "solution". Due to my lack of knowledge tho, I cant get it to work.
The code I have found:
function replaceUndefined(displayWeather){
if(typeof(displayWeather) === "undefined"){
return "0"; // return 0 as replace, and end function execution
}
return displayWeather; // if the above state is false, functions continues and return original value
};
Now, here is my whole JAVASCRIPT code:
let weather = {
apiKey: "χχχχχχχχχχχχχχχ",
fetchWeather: function (city) {
fetch(
"https://api.openweathermap.org/data/2.5/weather?q=" +
city +
"&units=metric&lang=el&appid=" +
this.apiKey
)
.then((response) => {
if (!response.ok) {
alert("No weather found.");
throw new Error("No weather found.");
}
return response.json();
})
.then((data) => this.displayWeather(data));
},
displayWeather: function (data) {
const { name } = data;
const { lon } = data.coord;
const { lat } = data.coord;
const { icon, description } = data.weather[0];
const { temp, humidity } = data.main;
const {temp_min} = data.main;
const {temp_max} = data.main;
const { pressure } = data.main;
const { sea_level } = data.main;
const { speed } = data.wind;
const { deg } = data.wind;
const { sunrise } = data.sys;
const { sunset } = data.sys;
//getting the data from the JSON API file.
document.querySelector(".city").innerText = "Καιρός: " + name;
// (".city") is the class name from the DIV in HTML file.
document.querySelector(".lon").innerText = "Γεωγραφικό μήκος (longitude): " + lon;
document.querySelector(".lat").innerText = "Γεωγραφικό πλάτος (latitude): " + lat;
document.querySelector(".icon").src =
"https://openweathermap.org/img/wn/" + icon + ".png";
document.querySelector(".description").innerText = description;
document.querySelector(".temp").innerText = temp + "°C";
document.querySelector(".temp_min").innerText = "Ελάχιστη θερμοκρασία (αυτην την στιγμή): " + temp_min + "°C";
document.querySelector(".temp_max").innerText = "Μέγιστη θερμοκρασία (αυτην την στιγμή): " + temp_max + "°C";
document.querySelector(".humidity").innerText =
"Υγρασία: " + humidity + "%";
document.querySelector(".pressure").innerText = "Πιέση: " + pressure;
document.querySelector(".sea-pressure").innerText = "Ατμοσφαιρική πίεση στο επίπεδο της θάλασσας: " + sea_level;
document.querySelector(".wind").innerText =
"Ταχύτητα αέρα: " + speed + " km/h";
document.querySelector(".wind-deg").innerText = "Κατεύθυνση του αέρα (degrees): " + deg;
document.querySelector(".sunrise").innerText = "Ανατολή Ήλιου: " + window.moment(sunrise * 1000).format('HH:mm a');
document.querySelector(".sunset").innerText = "Δύση Ήλιου: " + window.moment(sunset * 1000).format('HH:mm a');
document.querySelector(".weather").classList.remove("loading");
document.body.style.backgroundImage =
"url('https://source.unsplash.com/1600x900/?')";
},
search: function () {
this.fetchWeather(document.querySelector(".search-bar").value);
},
};
document.querySelector(".search button").addEventListener("click", function () {
weather.search();
});
document
.querySelector(".search-bar")
.addEventListener("keyup", function (event) {
if (event.key == "Enter") {
weather.search();
}
});
weather.fetchWeather("London");
//Loads weather of London when the page opens
(some texts in the code are in greek because I want the text that is displayed on the page, to be in that language)
Thank you. Please I need solution.
document.querySelector(".sea-pressure").innerText = "Ατμοσφαιρική πίεση στο επίπεδο της θάλασσας: " + sea_level || 'not found';
Or just for hide all the line if data doesn't exist
if(seal_level){
document.querySelector(".sea-pressure").innerText = "Ατμοσφαιρική πίεση στο επίπεδο της θάλασσας: " + sea_level;
}

Use Cloudflare HTMLRewriter to modify multiple different element types

I'd like to use Cloudflare's HTMLRewriter to modify a <p> tag with some text, and modify an <img> tag with a different src.
I've read the docs here: https://developers.cloudflare.com/workers/runtime-apis/html-rewriter but I cannot find any examples addressing re-writing multiple different elements.
I'm using Cloudflare Pages Functions so as far as I can tell I do need to do all of this in the same file.
My code currently looks like this, to just do the re-write on the <p> tag. It works, but I'd like to update a weather icon as well in the menu bar.
// ./functions/index.js
export const onRequestGet = async ({ request, next }) => {
try{
// Get the static asset response
const response = await next()
const { latitude, longitude, city, country, timezone } = request.cf
let endpoint = "https://api.openweathermap.org/data/2.5/weather?"
endpoint += "lat=" + latitude + "&lon=" + longitude + "&appid=APPID"
const init = {
headers: {
// "User-Agent" : "EMAIL",
},
}
const responseWeather = await fetch(endpoint,init)
const content = await responseWeather.json()
const currd = new Date()
var humanTime = currd.toLocaleTimeString('en-US', { timeZone: timezone })
//Get the value from the object
const currentTempC = content.main.temp
const weatherDescription = content.weather[0].description
const currentTempF = Math.round(((9/5)* (currentTempC - 273)) + 32)
var currentTempLocal
var degreesSymbol
switch(country) {
case "US":
case "BS":
case "KY":
case "LR":
case "PW":
case "FM":
case "MH":
currentTempLocal = currentTempF
degreesSymbol = "°F"
break;
default:
currentTempLocal = currentTempC
degreesSymbol = "°C"
break;
}
// US BS KY LR PW FM MH
const weatherString = "At " + humanTime + " in " + city + "there's " + weatherDescription + " and the temperature is " + currentTempLocal + degreesSymbol + "."
// var errorReport = timezone + "\n" + humanTime + "\n" + JSON.stringify(context)
// Find the placeholder in our static asset
return new HTMLRewriter().on('#weather', {
// And act on the element
element(element) {
// https://developers.cloudflare.com/workers/runtime-apis/html-rewriter#methods
element.setInnerContent(weatherString, { html: true })
}
}).transform(response)
} catch (thrown){
return new Response(thrown);
}
}
This should be as simple as chaining another .on method to your HTMLRewritter like so -
return new HTMLRewriter().on('#weather', {
element(element) {
// first handler
}).on('img', {
element(element) {
// second handler
}).transform(response)

batch rest call in sharepoint online

I am trying to understand how the batch rest calls work.
I could not find any simple example on the internet. I have found the examples from https://github.com/andrewconnell/sp-o365-rest but can't run these examples or I have no idea how yet. I am guessing you have to deploy the app to a sharepoint site.
Given that, I am just looking for the simplest example of a add list item and update list item in bulk/batch. Also if anyone knows how I can make the app from that git to run will be really appreciated.
Thanks.
The github project is a add-in project so you need deploy the add-in project, then you can use it.
You could check below script from here.
My test result in this thread
(function () {
jQuery(document).ready(function () {
jQuery("#btnFetchEmployees").click(function () {
addEmployees();
});
});
})();
function addEmployees() {
var employeesAsJson = undefined;
employeesAsJson = [
{
__metadata: {
type: 'SP.Data.EmployeeInfoListItem'
},
Title: 'Geetanjali',
LastName: 'Arora',
Technology: 'SharePoint'
},
{
__metadata: {
type: 'SP.Data.EmployeeInfoListItem'
},
Title: 'Geetika',
LastName: 'Arora',
Technology: 'Graphics'
},
{
__metadata: {
type: 'SP.Data.EmployeeInfoListItem'
},
Title: 'Ashish',
LastName: 'Brajesh',
Technology: 'Oracle'
}
];
addEmployeeInfoBatchRequest(employeesAsJson);
}
function generateUUID() {
var d = new Date().getTime();
var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
var r = (d + Math.random() * 16) % 16 | 0;
d = Math.floor(d / 16);
return (c == 'x' ? r : (r & 0x7 | 0x8)).toString(16);
});
return uuid;
};
function addEmployeeInfoBatchRequest(employeesAsJson) {
// generate a batch boundary
var batchGuid = generateUUID();
// creating the body
var batchContents = new Array();
var changeSetId = generateUUID();
// get current host
var temp = document.createElement('a');
temp.href = _spPageContextInfo.webAbsoluteUrl;
var host = temp.hostname;
// iterate through each employee
for (var employeeIndex = 0; employeeIndex < employeesAsJson.length; employeeIndex++) {
var employee = employeesAsJson[employeeIndex];
// create the request endpoint
var endpoint = _spPageContextInfo.webAbsoluteUrl
+ '/_api/web/lists/getbytitle(\'EmployeeInfo\')'
+ '/items';
// create the changeset
batchContents.push('--changeset_' + changeSetId);
batchContents.push('Content-Type: application/http');
batchContents.push('Content-Transfer-Encoding: binary');
batchContents.push('');
batchContents.push('POST ' + endpoint + ' HTTP/1.1');
batchContents.push('Content-Type: application/json;odata=verbose');
batchContents.push('');
batchContents.push(JSON.stringify(employee));
batchContents.push('');
}
// END changeset to create data
batchContents.push('--changeset_' + changeSetId + '--');
// batch body
var batchBody = batchContents.join('\r\n');
batchContents = new Array();
// create batch for creating items
batchContents.push('--batch_' + batchGuid);
batchContents.push('Content-Type: multipart/mixed; boundary="changeset_' + changeSetId + '"');
batchContents.push('Content-Length: ' + batchBody.length);
batchContents.push('Content-Transfer-Encoding: binary');
batchContents.push('');
batchContents.push(batchBody);
batchContents.push('');
// create request in batch to get all items after all are created
endpoint = _spPageContextInfo.webAbsoluteUrl
+ '/_api/web/lists/getbytitle(\'EmployeeInfo\')'
+ '/items?$orderby=Title';
batchContents.push('--batch_' + batchGuid);
batchContents.push('Content-Type: application/http');
batchContents.push('Content-Transfer-Encoding: binary');
batchContents.push('');
batchContents.push('GET ' + endpoint + ' HTTP/1.1');
batchContents.push('Accept: application/json;odata=verbose');
batchContents.push('');
batchContents.push('--batch_' + batchGuid + '--');
batchBody = batchContents.join('\r\n');
// create the request endpoint
var endpoint = _spPageContextInfo.webAbsoluteUrl + '/_api/$batch';
var batchRequestHeader = {
'X-RequestDigest': jQuery("#__REQUESTDIGEST").val(),
'Content-Type': 'multipart/mixed; boundary="batch_' + batchGuid + '"'
};
// create request
jQuery.ajax({
url: endpoint,
type: 'POST',
headers: batchRequestHeader,
data: batchBody,
success: function (response) {
var responseInLines = response.split('\n');
$("#tHead").append("<tr><th>First Name</th><th>Last Name</th><th>Technology</th></tr>");
for (var currentLine = 0; currentLine < responseInLines.length; currentLine++) {
try {
var tryParseJson = JSON.parse(responseInLines[currentLine]);
$.each(tryParseJson.d.results, function (index, item) {
$("#tBody").append("<tr><td>" + item.Title + "</td><td>" + item.LastName + "</td><td>" + item.Technology + "</td></tr>");
});
} catch (e) {
}
}
},
fail: function (error) {
}
});
}

Categories

Resources