Non max rectangle when using d3plus largestRect - d3.js

When I use d3plus.largestRect it often doesnt return the largest inscribed rectangle. Any ideas whats going on, or what I should do to try to debug?
Someone took it from d3plus and made it its own repo here if its helpful.
https://github.com/rossjs/largest-rect-in-poly-geojson
export function maxRect(polygon, userOptions) {
// polygon is a geojson polygon and aspectRatio is a number that is the ratio of width to height
// we want to return a geojson polygon that is the maximum rectangle that fits inside the polygon
// and has the given aspect ratio
// we need to convert the geojson polygon to an array of points
var points = polygon.coordinates[0].map(function (coord) {
return [coord[0], coord[1]];
});
if (userOptions) {
console.log("userOptions", userOptions)
var options = userOptions;
} else {
var options = {
};
}
// we use d3plus to calculate the rectangle
// aspect ratio is the ratio of width to height
// var rect = largestRectInPoly(points, options);
var rect = largestRect(points, options);
console.log(rect)
return rect;
}
Example where rectangle is wrong. This happens regardless of which options I pass to it
{
"type": "Polygon",
"coordinates": [
[
[
-121.498361304,
38.622343198
],
[
-121.498593986,
38.622343198
],
[
-121.498593986,
38.622508322
],
[
-121.498565407,
38.622508355
],
[
-121.498565823,
38.622465787
],
[
-121.498410255,
38.622463692
],
[
-121.498409129,
38.622508534
],
[
-121.498361664,
38.622508588
],
[
-121.498361304,
38.622343198
]
]
]
}
Some examples:
So I did some digging and it looks like it starts erroring before event 3, which should be a modifOrigin. Investigating what that is.

Related

how to put threejs building on mapbox to its real place

currently i've load eiffel tower obj file, and render it using threejs, but how can i put the building on map to its place in real world. i use mapgox-gl-js to handle map issues, for its convenience on 3d map.
style: {
"version": 8,
"sources": {
"satellite": {
"type": "raster",
"url": "mapbox://mapbox.satellite",
"tileSize": 256
},
"canvas": {
type: 'canvas',
canvas: 'idOfMyHTMLCanvas',
// animate: true,
coordinates: [
[-74.02204952394804, 40.706782422418456],
[-73.99115047610259, 40.706782422418456],
[-73.99115047610259, 40.72021689994298],
[-74.02204952394804, 40.72021689994298]
],
contextType: 'webgl'
}
},
"layers": [{
"id": "satellite",
"type": "raster",
"source": "satellite"
}, {
"id": "video",
"type": "raster",
"source": "canvas"
}]
}
thank you for any help.
You may want to check out Threebox, which is designed to sync a Three.js scene graph with a Mapbox GL JS map.
This question is quite old, but indeed as suggested by #lauren-budorick, it took me 5 minutes to do this sample using the latest version of threebox and the result is like this
<script>
mapboxgl.accessToken = 'PASTE HERE YOUR TOKEN';
var origin = [2.294514, 48.857475];
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/satellite-v9',
center: origin,
zoom: 18,
pitch: 60,
bearing: 0
});
map.on('style.load', function () {
map
.addLayer({
id: 'custom_layer',
type: 'custom',
renderingMode: '3d',
onAdd: function (map, mbxContext) {
window.tb = new Threebox(
map,
mbxContext,
{
defaultLights: true,
}
);
// import tower from an external glb file, downscaling it to real size
// IMPORTANT: .glb is not a standard MIME TYPE, you'll have to add it to your web server config,
// otherwise you'll receive a 404 error
var options = {
obj: '/3D/eiffel/eiffel.glb',
type: 'gltf',
scale: 0.01029,
units: 'meters',
rotation: { x: 0, y: 0, z: 0 }, //default rotation
adjustment: { x: -0.5, y: -0.5, z: 0 } // place the center in one corner for perfect positioning and rotation
}
tb.loadObj(options, function (model) {
model.setCoords(origin); //position
model.setRotation({ x: 0, y: 0, z: 45.7 }); //rotate it
tb.add(model);
})
},
render: function (gl, matrix) {
tb.update();
}
});
})
</script>
I just stumbled across this question and wanted to provide an updated answer for anyone else who ends up here. At the time the question was asked, this was not possible in Mapbox GL JS without a plugin but it can be achieved now with the CustomLayerInterface.
Here's an example of adding a Three.js model to a Mapbox GL JS map.

NVD3 stacked bar chart option not working

I am trying to create bar chart using nvd3 data. Grouped option is working fine but when I select Stacked it gives following error.
Uncaught TypeError: Cannot read property '1' of undefined(…)
JSON format is as below.
var test = [
{
"key":"A",
"values":
[
{"x":"2016-11-24","y":34},
{"x":"2016-11-25","y":10}
]
},
{
"key":"B",
"values":
[
{"x":"2016-11-25","y":15}
]
},
{
"key":"C",
"values":
[
{"x":"2016-11-28","y":11}
]
},
]
javascript code:
var chart;
nv.addGraph(function() {
chart = nv.models.multiBarChart()
.color(d3.scale.category10().range())
.rotateLabels(0) //Angle to rotate x-axis labels.
.transitionDuration(300)
.showControls(true) //Allow user to switch between 'Grouped' and 'Stacked' mode.
.groupSpacing(0.24) //Distance between each group of bars.
;
chart.reduceXTicks(false).staggerLabels(true).groupSpacing(0.3);
chart.x(function(d) { return d.x; });
chart.y(function(d) { return d.y; });
d3.select('#chart1 svg')
.datum(test)
.call(chart);
nv.utils.windowResize(chart.update);
return chart;
});
I have tried it but can't find answer. Any help ?
Answer is in the JSON data. Basically the values array should have the same length across all data series. In your example when nvd3 transforms data into stacked view it expects to have a 2nd element of the values array.
{
"key":"B",
"values":
[
{"x":"2016-11-25","y":15}
]
}

How to specify an "id" for a path in Nvd3

suppose this is my data
data = [
{
"key": "Series1",
"values": [ [ 1125409600000 , 0] , [ 1228088000000 , 50]]
},
{
"key": "Series2",
"values": [ [ 1025409600000 , 10] , [ 1028088000000 , 50]]
}
]
I am using Nvd3 to draw a line chart. So based on the data given above I should have two lines.
nv.addGraph(function() {
var chart = nv.models.lineWithFocusChart();
d3.select('#chart svg')
.datum(data)
.transition().duration(500)
.call(chart);
nv.utils.windowResize(chart.update);
return chart;
});
I can easily do this and my code works. After drawing the chart, each line on the chart is path in the DOM. I am trying to assign each KEY value as an id of the corresponding path. For example, after drawing the graph I want to have something like this
<path id="Series1" ..... >
<path id="Series2" ..... >
Does anyone know how to do this?
As #baklazan states in his comment, the group around the path has a class of nv-series-0, nv-series-1, etc... If you can just use that and get the corresponding path like:
var series1Path = d3.select('.nv-series-0>path');
If you really want to id them:
d3.select('.nv-series-0>path')
.attr('id','Series1);

Render bands / bandData in jqplot when series contains null values

I am using jqplot to render a line graph.
My series data looks like:
results =[
['1/1/2014', 1000],
['2/1/2014', 2000],
['3/1/2014', 3000],
['4/1/2014', 4000],
['5/1/2014', null]
];
my call to jqplot looks something like
$.jqplot('myChart', results,
{
series: [
{
rendererOptions: {
bands: {
show: true,
interval: '10%'
},
}
}
]
});
The chart will render, but it will be missing the 10% bands above and below.
If i change the null value
['5/1/2014', null]
to be
['5/1/2014', 5000]
then the bands will render correctly.
My does data have some missing values. Is there any way to make the bands render for non-null data points on the line, even if the line does have some null data points?
Instead of sending null for those values, omit them entirely and depend on the dateAxisRenderer to correctly space the values on the axis.
results = [
['1/1/2014', 1000],
['2/1/2014', 2000],
['3/1/2014', 3000],
['4/1/2014', 4000],
['6/1/2014', 3500]
];
$.jqplot ('myChart', [results],
{
series: [{
rendererOptions: {
bands: {
show: true,
interval: '10%'
}
}
}],
axes: {
xaxis: {
renderer: $.jqplot.DateAxisRenderer
}
}
});
JSFiddle version here (don't forget the extra script reference for dateAxisRenderer)
http://jsfiddle.net/4vmNf/1/
Alternatively, you can pass separate arrays for upper & lower band data, and this does not have to follow the same intervals as the underlying data array.
lowerBand = [];
upperBand = [];
for(var i = 0; i < results.length; i++) {
if (results[i][1]) {
lowerBand.push([results[i][0], results[i][1] * 0.9]);
upperBand.push([results[i][0], results[i][1] * 1.1]);
} else {
// not clear to me how you want to calculate band for missing values
lowerBand.push([results[i][0], 3500]);
upperBand.push([results[i][0], 6500]);
}
}
And then use bandData option:
series: [{
rendererOptions: {
bandData: [lowerBand, upperBand]
}
}]

Force ticks to be whole numbers

Exactly the same as this question, but since I don't have the reputation to upvote it (and can't answer it), I'm going to have to ask it again...
Given a graph like the following, how can I force the ticks to always be whole numbers, even if the graph is empty? At the moment, if there is no data for the graph, it will go 0, 0.2, 0.4, 0.6, 0.8, 1.0, but in the context of this graph, that makes no sense - what is being displayed can only consist of whole numbers. If the numbers involved are very small, it can occasionally display as, e.g., 1.0, 1.5, 2.0, 2.5, 3.0, 3.5 (etc), which again is not ideal. As the graph represents things that have happened over the course of the day, it will start out at zero each day and slowly grow, resetting to zero the next day. Ideally, I would like the ticks to start out at 0, 1, 2, 3, 4, 5, and then scale appropriately with the data.
Any suggestions? I've tried tickInterval: 1 (and 5 and 50...) in both the X and Y axes, but that didn't seem to do anything... Maybe a bug in JQPlot?
EDIT: JSFiddle Example showing the problem.
$.jqplot('chartdiv', [ data.a, data.b ], {
title : "Title",
stackSeries : true,
seriesDefaults : {
renderer : $.jqplot.BarRenderer,
rendererOptions : {
barMargin : 15,
barDirection : 'horizontal'
},
pointLabels : {
show : true,
stackedValue : false
},
},
seriesColors : [ "#651811", "#126542" ],
axesDefaults : {
tickRenderer : $.jqplot.CanvasAxisTickRenderer,
tickOptions : {
fontSize : '8pt'
}
},
axes : {
xaxis : {
label : "X-Label",
},
yaxis : {
renderer : $.jqplot.CategoryAxisRenderer,
ticks : [ "T1", "T2", "T3", "T4" ],
tickOptions : {
angle : -75
},
label : "Y-Label",
labelRenderer : $.jqplot.CanvasAxisLabelRenderer,
labelOptions : {
angle : -90
},
}
},
highlighter : {
show : false,
showTooltip : false,
},
legend : {
show : true,
location : 'e',
placement : 'outside',
labels : [ 'L1', 'L2' ]
}
});
You might have better luck setting the max and min values for the axis, then let JQPlot fill in the tick values.
// This is YOUR data set
var series = [882, 38, 66, 522, 123, 400, 777];
var max;
if (series.length > 0) {
// Instead of letting JQPlot pick the scale on the y-axis, let's figure out our own.
var series_max = Math.max.apply(null, series);
var digits = max.toString().length;
var scale = Math.pow(10, max_digits - 1);
max = (Math.ceil(series_max / scale)) * scale;
} else {
// Since you sometimes have an empty dataset, then just pick an arbitrary max.
max = 10;
}
$.jqplot(
'foo',
[series],
{
axes: {
yaxis: {
min: 0,
max: max
}
}
// Put your other config stuff here
}
)
This basic approach works for any integers. If you are using floats and they are greater than 1, you can still use this approach by casting your "series_max" to int. If you are using floats less than 1, this approach may not be suitable: it will always pick 1 as the y-axis max.

Resources