How can I add levels to a JSON array? - javascript

I have a JSON array of 7,000 objects, which are all under one parent. I want to add two levels in front of them, a year level, then a month level.
Currently my structure looks like:
{
//apod is single representation of main parent
"apod" : {
//below is a unique identifier with nested data
"-KgpW6owlA2YGwKFj2V-" : {
"date" : "1995-06-16",
"explanation" : "If the Earth could somehow be transformed to the ultra-high density of a neutron star , it might appear as it does in the above computer generated figure. Due to the very strong gravitational field, the neutron star distorts light from the background sky greatly. If you look closely, two images of the constellation Orion are visible. The gravity of this particular neutron star is so great that no part of the neutron star is blocked from view - light is pulled around by gravity even from the back of the neutron star.",
"gsHdUrl" : "https://www.foo.com",
"gsThumbnailUrl" : "https://www.foo.com",
"hdurl" : "https://apod.nasa.gov/apod/image/e_lens.gif",
"media_type" : "image",
"service_version" : "v1",
"title" : "Neutron Star Earth",
"url" : "https://apod.nasa.gov/apod/image/e_lens.gif"
},
//below is another unique identifier with nested data
"-KgpW7fV9laX8YL30glD" : {
"date" : "1995-06-20",
"explanation" : "Today's Picture: June 20, 1995 The Pleiades Star Cluster Picture Credit: Mount Wilson Observatory Explanation: The Pleiades star cluster, M45, is one of the brightest star clusters visible in the northern hemisphere. It consists of many bright, hot stars that were all formed at the same time within a large cloud of interstellar dust and gas. The blue haze that accompanies them is due to very fine dust which still remains and preferentially reflects the blue light from the stars. We keep an archive of previous Astronomy Pictures of the Day. Astronomy Picture of the Day is brought to you by Robert Nemiroff and Jerry Bonnell . Original material on this page is copyrighted to Robert J. Nemiroff and Jerry T. Bonnell.",
"gsHdUrl" : "https://www.foo.com",
"gsThumbnailUrl" : "https://www.foo.com",
"hdurl" : "https://apod.nasa.gov/apod/image/pleiades2.gif",
"media_type" : "image",
"service_version" : "v1",
"title" : "Pleiades Star Cluster",
"url" : "https://apod.nasa.gov/apod/image/pleiades2.gif"
}
}
What I want to transform it to would look like:
{
"apod": {
"1995": {
"06": {
"-KgpW6owlA2YGwKFj2V-": {
"date": "1995-06-16",
"explanation": "If the Earth could somehow be transformed to the ultra-high density of a neutron star , it might appear as it does in the above computer generated figure. Due to the very strong gravitational field, the neutron star distorts light from the background sky greatly. If you look closely, two images of the constellation Orion are visible. The gravity of this particular neutron star is so great that no part of the neutron star is blocked from view - light is pulled around by gravity even from the back of the neutron star.",
"gsHdUrl": "https://www.foo.com",
"gsThumbnailUrl": "https://www.foo.com",
"hdurl": "https://apod.nasa.gov/apod/image/e_lens.gif",
"media_type": "image",
"service_version": "v1",
"title": "Neutron Star Earth",
"url": "https://apod.nasa.gov/apod/image/e_lens.gif"
},
"-KgpW7fV9laX8YL30glD": {
"date": "1995-06-20",
"explanation": "Today's Picture: June 20, 1995 The Pleiades Star Cluster Picture Credit: Mount Wilson Observatory Explanation: The Pleiades star cluster, M45, is one of the brightest star clusters visible in the northern hemisphere. It consists of many bright, hot stars that were all formed at the same time within a large cloud of interstellar dust and gas. The blue haze that accompanies them is due to very fine dust which still remains and preferentially reflects the blue light from the stars. We keep an archive of previous Astronomy Pictures of the Day. Astronomy Picture of the Day is brought to you by Robert Nemiroff and Jerry Bonnell . Original material on this page is copyrighted to Robert J. Nemiroff and Jerry T. Bonnell.",
"gsHdUrl": "https://www.foo.com",
"gsThumbnailUrl": "https://www.foo.com",
"hdurl": "https://apod.nasa.gov/apod/image/pleiades2.gif",
"media_type": "image",
"service_version": "v1",
"title": "Pleiades Star Cluster",
"url": "https://apod.nasa.gov/apod/image/pleiades2.gif"
}
},
"07": {}
},
"1996": {}
}
}
I want to keep the key->object mapping. What would be the best way to loop through this and restructure it? I'm somewhat lost as I haven't worked with editing/restructuring JSON that much.
Thank you

What you have are objects, not arrays. Anyway, I commented things out.
function convert( json ){
// `data` is a JavaScript object we get from parsing
// json. For simplicity we remove the `apod` property
// and add it prior to returning.
let data;
try {
data = JSON.parse(json).apod;
}
catch(e){
alert("invalid json!");
}
// Create a new object to store the mapped objects.
// We will convert this to a json string in the end.
const result = {};
// Iterate all properties.
const keys = Object.keys(data);
for( let key of keys ){
// Parse month and year.
const [year, month] = data[key].date.split("-", 2);
// If there hadn't been an occurrence of this year,
// introduce an object.
if( !result[year] )
result[year] = {};
// If there hadn't been an occurrence of this month
// in this year, introduce an object.
if( !result[year][month] )
result[year][month] = {};
// Add element to current year and month.
result[year][month][key] = data[key];
}
// Convert and return the mapped object as json.
return JSON.stringify({ apod: result });
}

{
"2015": {
"01": [{...}, {...}, ...],
"02": [{...}, ...],
...
"12": [...]
},
"2016": {
...
}
}
To loop via PHP you can:
foreach ($data as $year => $monthes) {
foreach ($monthes as $month => $objects) {
foreach ($objects as $object) {
// do something with $year, $month and $object
}
}
}

Related

Is there a way to merge multiple tiles in a level - Kaboom JS

When making a level in kaboomJS with a large tile map collisions, things start to get slow... So I was wondering if there was an easy way to merge multiple tiles like maybe a whole row of blocks could be treated as one large block?
1. Tiles don't have to fit in the grid
If you want to reduce the number of Game Objects in the Scene at a time you can have a single symbol in your level definition represent a Game Object that spans multiple grid tiles. So if you want a lot of platforms that are 3 grid squares wide, you don't need 3 objects per platform, you can just use a single character to represent a 3x1 rect:
import kaboom from "kaboom"
// initialize context
kaboom()
// load assets
loadSprite("bean", "sprites/bean.png")
addLevel(
// Note: the hyphens here hare just place holders to remind us that the
// game objects created by ➁ and ➂ are actually taking up 2 and 3 grid
// squares respectively.
[
" ⚥ ",
" ",
" ➂-- ",
" ",
" ➁- ",
" ",
" ",
"################################",
],
{
width: 32,
height: 32,
"⚥": () => [
sprite("bean"),
area(),
body(),
"player"
],
"#": () => [
rect(32, 32),
outline(2),
area(),
solid(),
],
"➁": () => [
rect(32 * 2, 32),
outline(2),
area(),
solid(),
],
"➂": () => [
rect(32 * 3, 32),
outline(2),
area(),
solid(),
],
}
);
const player = get("player")[0];
player.onUpdate(() => {
const left = isKeyDown('left') || isKeyDown('a');
const right = isKeyDown('right') || isKeyDown('d');
if (left && !right) {
player.move(-500, 0);
} else if (right && !left) {
player.move(500, 0);
}
camPos(player.pos);
});
onKeyPress("space", () => {
if (player.isGrounded()) {
player.jump();
}
});
Obviously if you had many different shapes and sizes this would be quite onerous.
2. Advanced: Quad Trees and Loading/Unloading Regions
I actually ran into this problem myself on a recent Kaboom project and decided to completely overhaul the built in addLevel() with my own implementation that loaded from a bitmap instead of a bunch of strings, and then organized the level data into a quadtree so that I could quickly find chunks that overlapped the visible area, and load and unload game objects based on their visibility. The technique and code are a bit to complex it include here, so I'll just link to the Repl and the relevant source code: level-loader.ts and lib-quad-tree.ts and the usage in level-one.js .

C3 stacked chart maintain data order

I'm following the instructions from the reference and from this answer, but am having no success.
I've got a stacked bar chart, I want the data in the stack to appear in the order it is in in the data, but it's getting re-ordered and I can't figure out how to prevent it.
Here's a jsfiddle.
Here's the code
var chartOptions = {
element: 'chart',
data: {
"rows":[["0-1","2-3","4-5","6","7"],[25,56,14,2,1],[6,66,27,0,0],[0,70,30,0,0],[27,54,14,5,0],[0,54,30,8,8],[29,64,7,0,0]],
"type":"bar",
"groups":[["0-1","2-3","4-5","6","7"]],
"order":null
},
axis: {
"x":{
"type":"category",
"categories":["Whole List","committed","community","congregation","core","crowd"]
},
"y":{
"padding":{
"top":0
}
}
},
stacked: true,
size: {"height":300}
}
var chart = c3.generate(chartOptions);
I would like the series to be in the order 0-1, 2-3, 4-5, etc
Turns out this is a known issue. If any one of the headers is an integer (or a string that is an integer), then c3 will sort the columns.
To work around I'm appending a space to the end of the column names so c3 interprets them as a string and not a number.
eg
chartOptions['data']['rows'][0] = ["0-1","2-3","4-5",
"6 ", "7 "]; // Spaces appended to these
chartOptions['data']["groups"] = [["0-1","2-3","4-5","6 ", "7 "]];
I've updated the jsfiddle with the workaround.

How to create next level of flow chart in d3js

I have created a basic flow chart but I am not getting that how to create next level of flow chart.
I am attaching the image and the jsfiddle link.
Fiddle
here is the data
"process":[
{
"ProcessName" : "nivprocess.exe",
"Username" : "Joh Doe",
"Created" : "10:00:00",
"processDescription" : null,
"process_parent" : null,
"operation" : [
{
"operationDescription":"OP1",
"operationTime":"10:15:15",
"response":"Fail"
},{
"operationDescription":"OP2",
"operationTime":"10:20:00",
"response":"Success"
}
],
},
{
"ProcessName" : "Process2.exe",
"Username" : "Some One",
"Created" : "11:00:00",
"processDescription" : null,
"process_parent" : "process1",
"operation" : [
{
"operationDescription":"OP1",
"operationTime":"10:15:15",
"response":"Fail"
},{
"operationDescription":"OP2",
"operationTime":"10:20:00",
"response":"Success"
}],
},
{
"ProcessName" : "Process3.exe",
"Username" : "Nika Boka",
"Created" : "12:00:00",
"processDescription" : null,
"process_parent" : "process2",
"operation" : [
{
"operationDescription":"OP1",
"operationTime":"10:15:15",
"response":"Fail"
},{
"operationDescription":"OP2",
"operationTime":"10:20:00",
"response":"Success"
}],
}
]}
You're drawing this manually (I assumed the flow chart meant a chart meaning a d3 layout), your data array has 3 data points so this will map to 3 drawn objects directly. I can see your code (which you should attach to the questions too) is drawing lines with two rects (under label text) and four pieces of text for each data point, however it's not processing any of the operation arrays in the data point.
An aside: I'm noticing some clipping, in JS fiddle it helped me to temporarily set the svg tag with a width:
<svg style="padding-top: 100px; width:100%" class="chart">
There's two approaches forward I might try:
Create an empty group associated with each process rect, var ops = chart.selectAll("g g"); then figure out the right way to get a reference to the data point bound to each parent g, lets refer to it by dp. Then do ops.selectAll("g").data(dp.operation).enter().append("g"); On each enter draw that first line to the fork. Then work with each operation within this groups' groups' group to draw the two operation lines, the operation circle and labels similar to work you did before. Notice I'm fuzzy on getting the reference to dp. It might be something like: bar.each(function(dp,i,t){d3.selectAll(t).data(dp.operation).enter().etc;});
Possibly manually setup the right selections. Assuming you made that empty second g like in the "append test", manual setup would look a bit like: data.forEach(function(dp, idx, arr){bar[idx].data(dp.operation).enter().etc;}) where I'm hoping the bar selectAll's indexes are in the same order as the data's. They will match in quantity.
While trying to go with #1, I ended up getting this to get part of the way to where you wanted. It certainly isn't pretty, but you get 1 line to a group for each process, then in each group 1 circle and 1 line for each operation (you'll have to add lines, arrows and labels, and it's a bit weird how I get the offsets):
//appending test
var ops = bar.append("g");
ops
.attr("transform", function(d, i) {
return "translate("+width+",0)";
});
ops
.append('line')
.attr("x1","0")
.attr("y1",lineHeight/2)
.attr("x2",width/8)
.attr("y2",lineHeight/2)
//.attr("transform","translate(0,-"+(lineHeight*.85)+")")
.attr("stroke","#FF0000")
.attr("stroke-width","4");
ops.each(
function(d,i,t){
if ('object'===typeof this) {
var op = d3.select(this).selectAll('g').data(d.operation).enter().append("g");
var xc=1;
op.append("circle")
.attr("cx",lineHeight)
.attr("cy",function(d){return lineHeight/2*xc++-lineHeight/4})
.attr("r",width/4)
.attr("fill", "#ff0000");
var xl1s=1;
var xl1e=1;
op.append("line")
.attr("x1",width/8)
.attr("y1",function(d){return lineHeight/2-width/4-lineHeight/4*xl1s++})
.attr("x2",lineHeight)
.attr("y2",function(d){return lineHeight/2-width/4-lineHeight/4*xl1e++})
.attr("stroke","#FF0000")
.attr("stroke-width","4");
}});

Calculating the percent overlap of two polygons in JavaScript

Here's the problem: I have geoJSON and topoJSON files that give me the polygons for Census block groups and voting precincts. I'm trying to see by how much a given Census block group overlaps with a given precinct.
I've seen a couple examples of what I'm after in other languages—i.e. R and in some GIS tools—but I'm trying to write this as a Node.js script. A few questions:
Is there an NPM module (I've done a fair amount of Googling, but I haven't found one) that can spit out the percent overlap?
Is there an algorithm, or an exmaple written in another language, that I should know about (I've looked, but I haven't the foggiest where to begin) and that I could port to JavaScript?
Failing these, can someone explain to me how I would go about thinking about creating an algorithm for this?
In the end, the final product would look something like this—imagining that I have arrays of precincts and blockgroups, and each one is an object with a geometry property that contains the polygon data for the precinct or block group, and also imagining that I have a function called overlap that, when passed two polygons spits out the percent overlap:
// Iterate over each precinct.
_.each( precincts, function ( precinct ) {
// Iterate over each blockgroup.
_.each( blockgroups, function ( blockgroup ) {
// Get the overlap for the current precinct and blockgroup.
var o = overlap( precinct.geometry, blockgroup.geometry );
// If they overlap at all...
if ( o > 0 ) {
// ...Add information about the overlap to the precinct.
precinct.overlaps.push({
blockgroup: blockgroup.id,
overlap: o
});
}
}
}
(I've seen this module, but that only gives if the polygons overlap, not by how much they do.)
turf-intersect takes two polygons and returns a polygon representing the intersection.
geojson-area takes a polygon and returns the area in square meters.
npm install turf
npm install geojson-area
var turf = require('turf');
var geojsonArea = require('geojson-area');
var poly1 = {
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [[
[-122.801742, 45.48565],
[-122.801742, 45.60491],
[-122.584762, 45.60491],
[-122.584762, 45.48565],
[-122.801742, 45.48565]
]]
}
}
var poly2 = {
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [[
[-122.520217, 45.535693],
[-122.64038, 45.553967],
[-122.720031, 45.526554],
[-122.669906, 45.507309],
[-122.723464, 45.446643],
[-122.532577, 45.408574],
[-122.487258, 45.477466],
[-122.520217, 45.535693]
]]
}
}
var intersection = turf.intersect(poly1, poly2);
var area_intersection = geojsonArea.geometry(intersection.geometry);
var area_poly1 = geojsonArea.geometry(poly1.geometry);
var percent_poly1_covered_by_poly2 = (area_intersection / area_poly1)*100;
To compute the overlapping percentage
Compute the intersection of the two polygons
Intersection = intersect(Precinct, Block)
Divide the area of Intersection by the area of the parent polygon of interest.
Overlap = area(Intersection) / area(Parent)
It is a little unclear what you mean by the percent overlap. The parent polygon could be one of several possibilities
a) area(Intersection) / area(Precinct)
b) area(Intersection) / area(Block)
c) area(Intersection) / area(Precinct union Block)
As for a javascript library, this one seems to have what you need Intersection.js
There's also the JSTS Topology Suite which can do geospatial processing in JavaScript. See Node.js examples here.
You can do spatial overlaps with Turf js. It has the features of JSTS (and more), but is very modular.

Force ChartJS to show Doughnut chart with null values

I have a grid with 4 doughtnut charts on each column for different periods of time: last 90 days, last 60 days, last 7 days and today.
The problem with today is that it doesn't always show data, especially in the morning. Is there a way to force ChartJS to show the chart even if it doesn't have any data?
Here's an example: http://jsfiddle.net/6xV78/219/
var pieData = [
{
value: 0,
color:"#3F9F3F"
},
{
value : 0,
color : "#222"
}
];
I found a quick work-around, not sure how "good" or "valid" way it is but it's working for me. If the values are null/zero I replaced them with -1 to retain the looks of the chart and then use the custom tooltip function to override the output.
{
...
data: [earned == 0 ? -1 : earned, pending == 0 ? -1 : pending]
...
},
options: {
tooltip: {
callbacks: {
label: function (tooltipItem, data) {
const value = data['datasets'][0]['data'][tooltipItem['index']];
return '$' + (value == -1 ? 0 : value);
}
}
}
}
Obviously I have 2 slices and when both are 0 the chart is displayed with 2 equal halves both showing $0 income (both earned and pending).
*Do note that this will still take 1 into account when others aren't null so you need to take care of that on your end. I added a method to verify if ALL values are null and that's the only case I display it like this.
A pie chart with all values equal to zero is ill-defined. Because the angle (and the area) of each slice is proportionate to the ratio of the slice's respective value over the sum of all values. If all values are equal to zero, then their sum is zero. Division by zero should be rightfully avoided by the library (hopefully by design), resulting in the no-pie-chart case you encounter. It should be the programmer's responsibility to detect the all-zeros case before populating the chart, if such a case has a possibility of occurring. Then the programmer could either display a "No data yet. What are you doing up so early? Go to sleep." message, if all values are zero. Or, maybe, if they terribly need to keep the looks consistent (having a chart there all the time), they could show a special all-black no-data pie chart with the following dummy data:
var pieNoData = [
{
value: 1,
color: "#000000"
}
];
There is no shame in disclosing that no data exists yet, though.

Categories

Resources