sending json to d3js graph - angularjs

I have a series of stored JSON objects(below) being sent out my html page via angular,
the issue I have is getting the information from angular out to my d3.js graph.(i think i need to convert them from JSON to normal values
for my x axis data to be graphed)
{ __v: 0, temp: 328, _id: 56e9417d6a8a307819c3e598 },
{ __v: 0, temp: 328, _id: 56e9417d6a8a307819c3e599 },
{ __v: 0, temp: 328, _id: 56e9417d6a8a307819c3e600 },
{ __v: 0, temp: 328, _id: 56e941826a8a307819c3e59a }
the below $http request received the data from app.js.
the y axis is sales(value of objects) and the x axis is hour(amount of entries from database to display)
$http.get('/solution-two/data3').success(function (data) {
var hour=$scope.salesData3.length+1;
var sales= data;
$scope.salesData3.push({hour: hour, sales:sales});
});
I have the array starting off with an array which gets the info sent in to the http request
$scope.salesData3=[
{hour: 1,sales: 280}];
salesData3 is the name of my d3.js graph, so in summary i think i need to defice my length of hour variable with the length of database entries
and sales variable needs to accept the JSON objects from the database, would i be correct?
thanks for reading

If I understood your question clearly, you are looking for the structure in which you can send the data in JSON to D3 ?
If you are plotting a line graph of sales vs hour, then you do not need to check for the length of the hour. Instead D3 has methods to detect the time format, which can be used to assign it to the X-Axis.
x = d3.scale.linear().range([0, width]);
y = d3.time.scale().range([height, 0]);
// Set the X axis domain with time
y.domain(d3.extent(salesData3, function (d) {
return d.sales;
}));
valueLine = d3.svg.line()
.x(function (d) {
return x(d.hour);
})
.y(function (d) {
return y(d.sales);
});
svg.append("path")
.attr("class", "CLASS_NAME")
.attr("id", curveId)
.attr("d", valueLine(salesData3));
This link has an example on how you can use a JSON array and pass it as a data to the x and y axis https://www.dashingd3js.com/using-json-to-simplify-code
Nice link which explains the task of formatting the time http://www.d3noob.org/2012/12/formatting-date-time-on-d3js-graph.html & http://jsfiddle.net/robdodson/KWRxW/

Related

Feeding array to json for use in turf.polygon, structure problems

I have a linestring and a polygon and I am using turf.booleanIntersect() to determine if the line goes through the polygon. The example that i have tested and works is:
var poly1 = turf.polygon([
[
[148.535693, -29.6],
[154.553967, -29.64038],
[154.526554, -33.820031],
[148.535693, -33.6],
[148.535693, -29.6]
]
]);
//const p1 = L.geoJSON(poly1).addTo(mymap);
console.log("TEST: " + turf.booleanIntersects(line, poly1));
In my real code I read the polygon values from a file and need to insert them into an array which needs to be converted into a "GeoJSON Feature or Geometry" (from webpage).
I am having trouble getting the array to json convert correct.
var polygonlines = [];
var start = [long,lat];
polygonlines.push([start]); //add multiple of these points to the to polygonlines array
//create my json
var geojsonPolygon =
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Polygon",
"coordinates": polygonlines
}
}
var turfpolygon = turf.polygon(geojsonPolygon.data.geometry.coordinates); //ERROR HERE
const p2 = L.geoJSON(turfpolygon).addTo(mymap);
var result = turf.booleanIntersects(line, turfpolygon)
The error I get is "Uncaught Error Error: Each LinearRing of a Polygon must have 4 or more Positions."
I can't quite get the structure of the geojsonPolygon correct. I think that it is look at geojsonPolygon Array(1) in attached picture instead of Array(10), but I can't work out how to fix it.
Would love some help getting this structure fixed. Thank you :)
p.s. please ignore values of lat/longs, just examples.
I have seen this question but it hasn't helped How to feed JSON data of coordinates to turf.polygon?
Ongoing answer..., content and code will be edited.
Here is a LIVE demo code that you can run to see how it works. It may help to answer you question.
Usage:
click Run code snippet button
click Full page in top-right corner to see map and console
//Using turf_polygon object style
var poly1 = turf.polygon([
[
[148.535693, -29.6],
[154.553967, -29.64038],
[154.526554, -33.820031],
[148.535693, -33.6],
[148.535693, -29.6]
]
]);
// Using geojson style data
// Coordinates are slightly different from poly1
// This can be used as a good example to compare/contrast with your implementation
// This geojson of poly2 works, you can see it on the map.
var poly2 = {
type: "Feature",
properties: { id: 102, name: "Poly_2" },
geometry: {
type: "Polygon",
coordinates: [
[
[148, -29],
[154, -29],
[154, -33],
[148, -33],
[148, -29]
]
]
}
};
var line12 = turf.lineString([[144, -30], [153, -31.8], [159, -32]]);
var line34 = turf.lineString([[144, -20], [160, -30]]);
/* Init and draw Leaflet map */
var map;
function initMap(coords, zoom) {
// initialize map container
map = L.map("mapid").setView(coords, zoom);
// get the stamen toner-lite tiles
var Stamen_Toner = L.tileLayer(
"https://stamen-tiles-{s}.a.ssl.fastly.net/toner-lite/{z}/{x}/{y}.{ext}",
{
attribution:
'Map tiles by Stamen Design, CC BY 3.0 — Map data © OpenStreetMap',
subdomains: "abcd",
minZoom: 0,
maxZoom: 20,
ext: "png"
}
);
// add the tiles to the map
Stamen_Toner.addTo(map);
//disable scroll wheel zoom
map.scrollWheelZoom.disable();
}
/* Leaflet use (lat,long) */
var coordinates = [-30, 150]; //[lat,long]
var zoom = 5;
initMap(coordinates, zoom);
//Add polygons and lines
L.geoJson(turf.featureCollection([poly1, poly2, line12, line34])).addTo(map);
// Intersection op
var result1 = turf.booleanIntersects(line12, poly1); //True
var result2 = turf.booleanIntersects(line34, poly1); //False
console.log(result1, result2);
//EOF
#mapid { height: 480px; width: 800px}
<link rel="stylesheet" href="https://unpkg.com/leaflet#1.3.4/dist/leaflet.css" integrity="sha512-puBpdR0798OZvTTbP4A8Ix/l+A4dHDD0DGqYW6RQ+9jxkRFclaxxQb/SJAWZfWAkuyeQUytO7+7N4QKrDh+drA==" crossorigin="" />
<!-- Make sure you put this AFTER Leaflet's CSS -->
<script src="https://unpkg.com/leaflet#1.3.4/dist/leaflet.js" integrity="sha512-nMMmRyTVoLYqjP9hrbed9S+FzjZHW5gY1TWCHA5ckwXZBadntCNs8kEqAWdrb9O7rxbCaA4lKTIWjDXZxflOcA==" crossorigin=""></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Turf.js/6.5.0/turf.min.js"></script>
<div id="mapid"></div>
How to get the polygonline array into geojson for use in turf polygon:
var polygonlines = [];
//these 2 lines occur multiple times in a loop
{
var start = [vol.lines[k].start.lng, vol.lines[k].start.lat];
polygonlines.push(start);
}
var geojsonPolygon =
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Polygon",
"coordinates": [polygonlines]
}
var turfpolygon = turf.polygon(geojsonPolygon.geometry.coordinates);
turf.booleanIntersects(line, turfpolygon)

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 insert annotation with auto y value

Is it possible to create annotations without knowledge of y value?
I have a chart with series and annotations which I want to show on the chart. But I only have the x value and want the annotation to have the same y value as the chart has on that x.
I tried to find the answer in the documentation, but haven't found it. I'm thinking of somehow fining the y value by knowing x, and then inserting the annotation at that point.
I created an example here
You should add this annotation dynamically inside the load callback inside which you can calculate the annotation position because this function is triggered after the chart has been initialized.
chart: {
events: {
load() {
let chart = this,
y;
chart.series[0].points.forEach((point) => {
if (point.x === 1572566400000) {
console.log(point);
y = point.y;
}
});
chart.addAnnotation({
shapeOptions: {},
shapes: [
{
src: "https://www.highcharts.com/samples/graphics/sun.png",
type: "image",
width: 30,
height: 30,
point: {
x: 1572566400000,
y: y,
xAxis: 0,
yAxis: 0
}
}
]
});
}
}
}
Demo: https://codesandbox.io/s/epic-kalam-9qzf3?file=/src/App.js
API: https://api.highcharts.com/class-reference/Highcharts.Chart#addAnnotation
API: https://api.highcharts.com/highcharts/chart.events.load

d3.selectAll enter exit of nested key value pairs -- aka: how do array selections work?

I have a nested array of data that I am using to create a grouped bar chart. I am having trouble assigning a key to the datajoin of the nested array.
Below is the input data, the working datajoin for the top level, and two partial implementations of what I am trying to accomplish. I am so close!
// input data
var data = [
{"key":"cat1","value":[{"key":"subcatA","value":100},{"key":"subcatB","value":200}]},
{"key":"cat2","value":[{"key":"subcatA","value":150},{"key":"subcatB","value":250}]}
];
To access the top level and build the main categories, I do this:
// top level (all good)
var plot = d3.select('#plot');
plot.selectAll(".category")
.data(data,function(d) {return d.key;}) // <-- return key on datajoin
.enter().append("g")
.attr("class", "category")
Returning d.key on the datajoin keeps track of each category such that enter() and exit() is not a giant mess of animation. See Mike Bostock's General Update Pattern, II for reference on adding a key to a datajoin.
To plot the second level, I do this:
// second level (plots but no key is assigned)
plot.selectAll(".category").selectAll(".bar")
.data(function (d) {return d.value; }) // <-- is this right?
.enter().append("rect")
.attr("class","bar")
There is no key assigned so d3 handles updates rather randomly. I can get what I want for only the first iteration if I do this:
// second level (key is assigned but first iteration only)
plot.selectAll(".g-category").selectAll(".bar")
.data(data[0].value, function (d) {return d.key; }) // <-- first category only
.enter().append("rect")
.attr("class","bar")
Perfection except I need to iterate over data, not just data[0]. My problem is as soon as I try, I get into a conflict of interest with d3.selectAll and make a general mess of things.
The answer to this question may be simple, but I am missing it: How do I go about properly selecting and assigning keys to nested arrays in d3?
The data method in your second level has just one argument:
.data(function (d) {
return d.value;
});
For assigning the key function you have to pass a second argument. For instance:
.data(function(d) {
return d.value
}, function(d) {
return d.key
});
You can better see it in one line:
.data(function(d) { return d.value }, function(d) { return d.key });
//2nd arg after the comma ----------^
However, to facilitate human reading, I'd advise you to use different property names for level 1 and level 2. Right now everything is value and key, which can be hard to understand (not for the machine, though). For instance, in the above snippet, value refers to the first level array, while key refers to the second level array, not the first one... do you see the mess?
Here is a demo with your data:
var data = [{
"key": "cat1",
"value": [{
"key": "subcatA",
"value": 100
}, {
"key": "subcatB",
"value": 200
}]
},
{
"key": "cat2",
"value": [{
"key": "subcatA",
"value": 150
}, {
"key": "subcatB",
"value": 250
}]
}
];
var body = d3.select("body");
var outer = body.selectAll(null)
.data(data, function(d) {
return d.key
})
.enter()
.append("div")
.attr("class", "outer");
var inner = outer.selectAll(null)
.data(function(d) {
return d.value
}, function(d) {
console.log("the key is: " + d.key)
return d.key
})
.enter()
.append("div")
.html(function(d) {
return d.key + " - " + d.value
});
<script src="https://d3js.org/d3.v4.min.js"></script>

Elasticsearch query parse failure

Why does this ES multi-match query return a 400 error (bad request)?
"query": {
"multi_match": {
"query": searchTerms,
"fields": ["content", "title"],
"operator": "and"
}
},
size: 100,
from: 0,
highlight: {
fields: {
"title": {number_of_fragments: 0},
"content": {number_of_fragments: 10,fragment_size: 300}
}
}
}
I'm using this query in conjunction with AngularJS UI Bootstrap Typeahead code like this
uib-typeahead="query as query._source.ymme for query in getSuggestions($viewValue)" typeahead-on-select="search($item)"
This is my search() function
$scope.search = function() {
console.log($scope.searchTerms);
$scope.currentPage = 0;
$scope.results.documents = [];
$scope.isSearching = true;
return searchService.search($scope.searchTerms, $scope.currentPage).then(function(es_return) {
var totalItems = es_return.hits.total;
var totalTime = es_return.took;
var numPages = Math.ceil(es_return.hits.total / $scope.itemsPerPage);
$scope.results.pagination = [];
for (var i = 1; i <= 100; i++) {
if(totalItems > 0)
$scope.results.totalItems = totalItems;
$scope.results.queryTime = totalTime;
$scope.results.pagination = searchService.formatResults(es_return.hits.hits);
$scope.results.documents = $scope.results.pagination.slice($scope.currentPage, $scope.itemsPerPage);
}
}
),
function(error){
console.log('ERROR: ', error.message);
$scope.isSearching = false;
}
};
I'm not quite sure what is wrong? I'm thinking it has something to do with $scope, but I'm not sure. The query works when I use it Sense plugin for ES and it also works if I just type in a search term instead of selecting it from the autocomplete dropdown.
If it is $scope, what am I missing?
UPDATE
All shards failed for phase: [query_fetch]
org.elasticsearch.search.SearchParseException: [hugetestindex][0]: from[-1],size[-1]: Parse Failure [Failed to parse source [{"query":{"multi_match":{"query":{"_index":"hugetestindex","_type":"doc","_id":"57","_score":3.877801,"_source":{"ymme":"bourne supremacy"}},"fields":["content","title"],"operator":"and"}},"size":100,"from":0,"highlight":{"fields":{"title":{"number_of_fragments":0},"content":{"number_of_fragments":10,"fragment_size":300}}}}]]
UPDATE 2 Object {_index: "hugetestindex", _type: "doc", _id: "56", _score: 2.5276248, _source: Object}
I think that is the problem, instead of a search terms, its receiving "Object"....?
UPDATE 3So basically it goes like this,
[Object, Object, Object, Object, Object]
0: Object
_id: "229"
_index: "hugetestindex"
_score: 3.3071127
_source: Object
ymme: "bourne supremacy"
__proto__: Object
_type: "doc"
__proto__:
Object1:
Object2:
Object3:
Object4:
Object
length: 5
__proto__: Array[0]
where "bourne supremacy" is the value of the ymme field in the _source Object, the array at the top with the 5 objects is the es return, es_return.hits.hits - this last hits, is the array.
The way you deconstruct your object is by doing something like the following:
object.data.hits.hits._source.field_name;
The above is only a notation to get the value of a single field, you might need to do a loop for each of those values so maybe something like:
$scope.list = []
for hit in object.data.hits.hits {
$scope.list.push(hit._source.field);
}
console.log(list);
Then from your HTML you want to use this list by doing an ng-repeat with it or something similar to get each of the terms in the list.
<div ng-repeat="term in list">{{term}}</div>
If you can update your question with how your object looks and what data you want from it, I can update this answer to match it exactly.
UPDATE
To match your data structure, I'm assuming you want to extract each of the ymme values from those objects. You need to do the following:
<div ng-repeat="object in es_return.hits.hits">
{{object._source.ymme}}
</div>
Just make sure "es_return" is a $scope variable so you can access it as above or just do:
$scope.es_return = es_return;
In your Angular code

Resources