How to get lat/lng coordinates from GEOMETRY('POINT') with Sequelize? - sql-server

I have an MS SQL database that contains a table with latitude/longitude positions of some devices. These positions are stored in a GEOGRAPHY field. Now I want to get these positions from my node app with Sequelize.
Here's my sequelize model definition:
db.define('StationLocation', {
StationId: { type: Sequelize.INTEGER },
Position: { type: Sequelize.GEOMETRY('POINT') }
}
)
And here's what I currently get:
{
"id":4,
"StationId":1,
"Position":{
"type":"Buffer",
"data":[
230,
16,
0,
0,
1,
12,
25,
159,
112,
57,
112,
200,
73,
64,
0,
0,
0,
128,
184,
247,
26,
64
]
}
}
How can I convert this to latitude and longitude values? Is Sequelize able to do this?
Update: In the example above, the position is this:
Latitude: 51.565924816122966
Longitude: 6.741914749145508
Here's my query:
StationLocation
.findAll({ raw: true })
.then(function(allStationLocations) {
console.dir(allStationLocations[0].coordinates); // Just for debugging
res.send(JSON.stringify(allStationLocations));
});
This translates to this SQL statement:
SELECT [id], [StationId], [Position] FROM [StationLocations] AS [StationLocation];

Proper support for MSSQL geometry and geography data types is still pending. Tedious does however provide a parser for that buffer you get as a result: Tediousjs Geoparser
As a workaround for anyone having the same problem i've created an adapter utility, it's fairly simple to use but only supports points: Sequelize MSSQL Adapter
Require and use accordingly in your model definition, more info on the snippet's description.
const adapter = require('../utils/sequelize-geodata-adapter');
module.exports = (sequelize, DataType) =>
sequelize.define('MyModel', {
// Attributes
location: {
type: DataType.GEOGRAPHY('POINT', 4326),
get: function () {
const location = this.getDataValue('location');
if (location)
return adapter.get(location);
},
set: function (value) {
if (value === undefined || value === null)
return this.setDataValue('location', value);
this.setDataValue('location', adapter.set(value));
},
validate: {
hasCoordinates: adapter.validate
}
},
...
}, {
tableName: 'MyModels'
...
});
Setter supports GeoJSON and Google-style {lat, lng} location formats, getter always returns {lat, lng} objects.
In your particular case you'll need to replace geography::Point with geometry::Point at the adapter since you are using the GEOMETRY data type. Parser is indistinct for both.

Related

display the output of math.max and math.min

I want to display the findMax() output in a div but im facing this problem :
'findMax' is not defined no-undef
, 'dataset' is not defined no-undef
Someone suggested that I need to use useState but I'm not sure how to make it work , I would appreciate any help !
const pricedata = {
datasets: [
{
backgroundColor: "#0000",
barPercentage: 2,
barThickness: 5,
data: [1, 10, 30, 7, 42, 12],
label: "Update in prices",
maxBarThickness: 10
},
{
backgroundColor: "#0000",
barPercentage: 2,
barThickness: 5,
data: [11, 70, 18, 17, 24, 12],
label: "Update in prices",
maxBarThickness: 10
}
]
};
function findMax(PRICES) {
if (!PRICES) {
return 0;
}
return Math.max(...PRICES);
}
pricedata.datasets.forEach((dataset) => {
dataset.maxPrice = findMax(dataset.data);
});
pricedata.datasets.forEach((dataset) => {
console.log('max price is', dataset.maxPrice);
});
return (
<div>{findMax(dataset.data)}</div>
There is no 'dataset' variable at the root level of the code. The pricedata variable contains an array called datasets. In your code when you call map, it loops over all the datasets - calling each individual one dataset. That variable name is only "scoped" to be within the map() function.
You can build the DIVs inside a map like this:
// call your findMax() on each element
const divs = pricedata.datasets.forEach((dataset,i) => (<div key={i}>{findMax(dataset.data)}</div>))
// using React, you'll need to surround the divs in a parent div
return <>{divs}</>

Leaflet: iterate through data for custom markers

I need your help with custom markers in Leaflet. I need custom markers that correspond with the data entries. Here is a data SAMPLE csv that looks like this:
result
display_na
lat
long
AV
14, Amsterdamer Straße, Leopoldkiez, Wedding, Mitte, Berlin, 13347, Deutschland
13.3574034
52.5517197
VK
Seestraße, Wedding, Mitte, Berlin, 13351, Deutschland
52.541301
13.3341968
This is my code by now
// create 3 types of markers in 3 colors
var LeafIcon = L.Icon.extend({
options: {
iconSize: [38, 95],
iconAnchor: [22, 94],
popupAnchor: [-3, -76],
},
});
// Read markers data from data.csv
$.get("./data.csv", function (csvString) {
// Use PapaParse to convert string to array of objects
var data = Papa.parse(csvString, { header: true, dynamicTyping: true }).data;
var greenIcon = new LeafIcon({ iconUrl: "greeb.png" }),
yellowIcon = new LeafIcon({ iconUrl: "yellow.png" }),
redIcon = new LeafIcon({ iconUrl: "red.png" });
// For each row in data, create a marker and add it to the map
// For each row, columns `Latitude`, `Longitude`, and `Title` are required
for (var i in data) {
var row = data[i];
var marker = L.marker([row.lat, row.long], {
opacity: 1,
}).bindPopup(row.display_na);
L.marker([row.lat, row.long])
.addTo(map)
.bindPopup(row.display_na)
.openPopup();
}
});
It's not working. Can you tell me where my failure lies? I have 3 types of markers
greenIcon
yellowIcon
redIcon
The color of the marker corresponds with the row result. If the result value in the csv is AV then the marker should be greenIcon. That's the idea.
Than you for, looking forward to your suggestions!
You are close. First you need a ternary or an if statement to check csv's result value as you mention when instantiating a marker. It accepts an object which has some options including icon key. Using that you can define a different icon apart from the predefined
for (var i in data) {
var row = data[i];
const marker = L.marker([row.lat, row.long], {
icon: row.result === "AV" ? greenIcon : blueIcon
})
.addTo(map)
.bindPopup(row.display_na)
.openPopup();
}
Moreover your csv should be in the form of:
result,display_na,lat,long
AV,14 Amsterdamer Straße Leopoldkiez Wedding Mitte Berlin 13347 Deutschland,52.5517197,13.3574034
VK,Seestraße Wedding Mitte Berlin 13351 Deutschland,52.541301,13.3341968
You need to have commas only when separating each column values otherwise it is considered a different column value.
Demo

How to create a query for multiple ranges?

I'm experiencing issues with how to create a multi range query.
So I'm using a library that creates a query using a setQuery function, which accepts an object. Scroll down to setQuery
Now currently my function creates a single range query and works fine. I followed the ES Docs so it matches what I have currently written, which is...
const queryObject = {
query: {
range: {
[searchType]: {
gte: from,
lte: to,
boost: 2.0,
},
},
},
value: queryItem,
};
But I can't seem to find any documentation for multiple ranges that has a similar shape.
Any recommendations on how to handle this?
Thanks!
Did it!
So let's say I have an array of ranges
const sampleRanges = [{from: 1990, to: 1992}, {from: 1993, to: 1995}, {from: 1996, to: 2002}]
I created a function to map over those values soo...
const generateRangeQuery = () => {
return sampleRanges.map(value => ({
range: {
[searchType]: {
gte: value.from,
lte: value.to,
boost: 2.0,
},
},
}));
};
And the query for multi ranges should then look like:
const query = {
query: {
bool: {
should: generateRangeQuery(),
minimum_should_match: 1,
boost: 1.0,
},
},
};
and this works for me!

In an array of obects watched with Vue-watch, how to get index of object that was changed?

I have an array of objects, like this:
myArray: [{
name: "First",
price: 10,
rebate: 5,
listPrice: 15,
outcome: 0
},{
name: "Second",
price: 11,
rebate: 5,
listPrice: 16,
outcome: 0
}
I want to recalculate the outcome-value whenever any of the other values in the same object change.
I already have a setup like this, but it looks for changes in any object and then recalculates the whole array. I've managed to set this up by using a combination of computed and watch functions. However they watch the whole array for changes and then recalculate the outcome-value for all objects in the array.
How can I watch for changes and then recalculate only the changed object?
Below is my current functions for recalculating the whole array (watching another property), but what I'm looking for could be completely different.
computed:
myArrayWasChanged() {
return [this.myArray.reduce((a, {vendors}) => a + vendors, 0), this.myArray.filter(item => item.discounted == false).length]
watch:
myArrayWasChanged: {
handler: function (val, oldVal) {
this.recalculateIsVendor();
Given the outcome is completely dependent on the other properties, it isn't really part of the component's state. Thus, in the component's data you could store the array without the outcome, and then calculate a new version of the array with the outcome as a computed property.
data: function () {
return {
myArrayWithoutOutcome: [
{
name: "First",
price: 10,
rebate: 5,
listPrice: 15
},
{
name: "Second",
price: 11,
rebate: 5,
listPrice: 16
}]
}
},
computed: {
myArrayWithOutcome: function () {
return this.myArrayWithoutOutcome.map(x => {
return {...x, outcome: this.calculateOutcome(x)}
})
}
},
methods: {
calculateOutcome(item) {
// Logic to calculate outcome from item goes here
return 0
}
}

Breeze Mongo Angular autoGeneratedKey error

I'm using Breeze with Angular and MongoDB.
I included all the necessary services and scripts to make sure breeze works with angular and MongoDB.
However, when I try to save my changes I get the following error on the server:
ObjectIds and Guids are the only autoGenerated key types that Breeze currently supports, not: undefined
This error occurs in the mongoSaveHandler.js file of the mongobreeze module:
var keyDataType = entityType.keyDataType;
if (keyDataType === "Guid") {
e._id = createGuid();
} else if (keyDataType == "MongoObjectId") {
// instead of omitting the _id and having mongo update it, we want to set it ourselves so that we can do
// fk fixup before going async
e._id = new ObjectID();
} else {
that._raiseError(new Error("ObjectIds and Guids are the only autoGenerated key types that Breeze currently supports, not: " + keyDataType));
return;
}
I made sure that the id of my object is a mongo id:
function addVisit() {
addType({
name: 'Visit',
dataProperties: {
id: { type: DT.MongoObjectId },
pain: { type: ID },
paper: {type: ID},
consistency: {type: ID}
}
});
}
But indeed when I log the entityType object it has no property keyDataType?
I can get everything to work if I just remove the error. Then my inserted objects look like this in MongoDB:
{ id: 5350d4e704a02e1f04000000,
pain: 50,
consistency: 50,
date: Fri Apr 18 2014 08:31:51 GMT+0100 (WEST),
_id: 5350d4e7101b04a9560e660a },
Meaning they have 2 ids?
When I try to query the database I get a nice response:
[
{
"id": "535052f504a02e79c6000000",
"pain": 50,
"consistency": 50,
"_id": "535052f6f672174a4dffffd4"
},
{
"id": "5350d1bb04a02e4e56000000",
"pain": 50,
"consistency": 50,
"date": "2014-04-18T07:18:19.616Z",
"_id": "5350d1bb101b04a9560e6606"
},
{
"id": "5350d2c104a02e595c000000",
"pain": 50,
"consistency": 50,
"date": "2014-04-18T07:22:41.696Z",
"_id": "5350d2c1101b04a9560e6607"
},
]
But somehow Breeze is unable to import this properly and I get a circular dependency.
Could this have something to do with the double ID's?
Where did you get DT.MongoObjectId from? That isn't listed in the breeze docs as a supported data type so it is returning undefined as a type. If you are properly generating the Id why not just use a string if it is immutable?
id : { type: DT.String }
Try to set a naming convention that would convert breeze's "id" field into mongo's "_id" and vice versa. It will eliminate double ID's
Here's the code for client side:
var convention = new breeze.NamingConvention({
serverPropertyNameToClient: function (serverPropertyName) {
switch (serverPropertyName) {
case '_id':
return 'id';
default :
return serverPropertyName;
}
},
clientPropertyNameToServer: function (clientPropertyName) {
switch (clientPropertyName) {
case 'id':
return '_id';
default:
return clientPropertyName;
}
}
});
convention.setAsDefault();

Resources