I'm trying to distort a d3.geo.path() map with the fisheye.js plugin (https://github.com/d3/d3-plugins/tree/master/fisheye).
To distort an object the plugin needs x & y attributes.
In the d3.js wiki it says:
A projection function takes a two-element array of numbers representing the coordinates of a location, [longitude, latitude], and returns a similar two-element array of numbers representing the projected pixel position [x, y]. For example, a rudimentary spherical Mercator projection:
https://github.com/mbostock/d3/wiki/Geo-Paths
So the distortion should be possible, I just can't wrap my head around it.
I'm using the world-50m.json for my projection. Once loaded there is an the arcs array. I think those are the coordinates I need to manipulate. But this is guesswork...
Thanks,
Kim
I found your post looking for the answer, and it doesn't appear to be out there on the internets. But, like you say, it's possible!
Following the documentation from the fisheye.js (https://github.com/d3/d3-plugins/tree/master/fisheye), in the mousemove callback you need to use fisheye on the coordinates.
Since fisheye uses the .x and .y attributes, I modified the fisheye code to just use the two pair [x,y] to avoid making that intermediate data structure every time in the callback.
Then you can do it like this:
canvas.on("mousemove", function() {
// console.log("mouse:");
// console.log(d3.mouse(this));
var here = d3.mouse(this);
// console.log(here); // [1030, 125]
// console.log(projection.invert(here)); // [-72.4713375653601, 45.14035261565636]
var inverted = projection.invert([here[0],here[1]]); // [-72.4713375653601, 45.14035261565636]
// console.log(inverted); // [-72.4713375653601, 45.14035261565636]
// burlington is lat 44, lon -73
fisheye.focus(inverted);
// of course, the path function takes [longitude, latitude], so -72, 44 for burlington
// https://github.com/mbostock/d3/wiki/Geo-Paths
// (so that's what it gives back)
states.attr("d",null)
.attr("d", function(d) {
// console.log("original:");
// console.log(d.geometry);
if (d.geometry.type === "Polygon") {
var b = d.geometry.coordinates.map(function(d) { return d.map(function(f) { return fisheye(f);}); });
}
else {
var b = d.geometry.coordinates.map(function(d) { return d.map(function(f) { return f.map(function(g) { return fisheye(g); }); }); });
}
// console.log(b);
var c = {type: d.geometry.type, coordinates: b};
// console.log("new:");
// console.log(c);
return path(c);
});
You can view a live version here: http://panometer.org/instruments/teletherms/?window=25&var=maxT&year=1914&city=BURLINGTON%20WSO%20AP,%20VT
Related
Given a point, say [-75.343, 39.984], how do I go about finding all features/markers within a 5km radius of it? I'm utilizing turf.js so I'll be using their circle() function to generate the circle about the point.
Would this work?
const center = [-75.343, 39.984];
const radius = 5;
const options = {steps: 10, units: 'kilometers', properties: {foo: 'bar'}};
const circle = turf.circle(center, radius, options);
}
const features = map.queryRenderedFeatures(
circle,
{ filter: {["within", circle] : true} }
);
I'm hoping to find all features within the circle and be able to store them in an array or in a database for further processing like accessing the feature's lat/lng, etc.
Thank you for the help!
Using queryRenderedFeatures you will be able to get the features that are actually in the viewport (visible). In case your source data is GeoJSON, you can use querySourceFeatures, so it will look to all your source features:
const filteredFeatures = map.querySourceFeatures('routes', {
filter: ['within', circle]
});
I am working on something to blacklist unwanted locations with the location service. Here is my current code:
How can I implement a blacklist feature?
if (navigator.geolocation) {
// Locate position
navigator.geolocation.getCurrentPosition(displayPosition, errorFunction);
} else {
alert('Your device location is not approved.');
}
// Success callback function
function displayPosition(pos) {
var mylat = pos.coords.latitude;
var mylong = pos.coords.longitude;
var thediv = document.getElementById('locationinfo');
alert('Your HWID is compliant of ProtoProt regulations.');
}
function errorFunction(pos) {
alert('Error: (PROTOPROT_POS_DENIED). We only use your HWID for checking compliance. Enable location to enter.');
}
Maintain a list of locations given by two points w,y.
Each w,y represent the two opposite points of a w,x,y,z square which represents the blacklisted location.
Each point has longitude and latitude coordinates, so w = [long, lat] and y = [long, lat]
With those, you can rebuild all the [long, lat] of all the w,x,y,z corners of your square, thus representing the blacklisted area.
Now, it's rather easy to know the boundaries of forbidden location: any point [long, lat] which is within the square is blacklisted.
You can store those values within a Javascript Object (a dictionary) which can be stored in a distinct ".js" file. JSON representation could look like:
blacklisted_areas = {
'area 51' : [w, y], // Replace w y with the floats of long and lat
'pink unicorn zoo' : [w, y], // same
// etc.
};
Access:
long = blacklisted_area['area 51'][0]
I'm pretty new to D3 and I'm trying to set point on a map.
I'm confused as I created a projection with this code:
var projection = d3.geo
.albersUsa()
.scale(500)
.translate([el.clientWidth / 2, el.clientHeight / 2]);
I use this projection to draw a map and it works fine.
But then whenever I call projection([10, 20]) it returns null whichever values I'm passing in.
What is my error?
From the documentation
# projection(location)
[…]
May return null if the specified location has no defined projected position, such as when the location is outside the clipping bounds of the projection.
The Albers USA projection is defined only within its borders and will yield null for coordinates outside the well-defined area.
See this comparison of calls to projection(location) using [10,20], which is not valid, and [-110,40], which is a valid point:
var projection = d3.geo
.albersUsa()
.scale(500)
.translate([960, 500]);
d3.selectAll("p")
.data([[10,20],[-110,40]])
.enter().append("p")
.text(function(d) { return d + ": " + projection(d); });
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
Considering the code given below -
var height = 500;
var data = [
{
"Product": "A",
"Branch1": 1200,
"Branch2": 2000
},
{
"Product": "B",
"Branch1": 1588,
"Branch2": 3495
}
];
var YAxisList = ["Branch1", "Branch2"];
var maxArray = [];
YAxisList.forEach(function (d) {
data.forEach(function (i) {
maxArray.push(i[d]);
});
});
var y = d3.scale.linear().range([height, 0]);
y.domain([d3.min(maxArray), d3.max(maxArray)]);
I wanted to extract the max and min to form the Y axis domain range. I've solved this by using the verbose way, but I suspect there has to be an elegant way to achieve this. Can somebody shed some light on this regard?
Another approach might employ some more of D3's functionality:
function getExtent(list) {
return d3.extent( // 4. Get the extent from that array
d3.merge( // 3. Merge all arrays into a single one
data.map(function(d) { // 2. Map each object's values to an array
return list.map(function(y) { // 1. Get the array of values to map
return d[y];
});
})
)
);
}
console.log(getExtent(["Branch1"])); // [1200, 1588]
console.log(getExtent(["Branch2"])); // [2000, 3495]
console.log(getExtent(["Branch1", "Branch2"])); // [1200, 3495]
y.domain(getExtent(yAxisList)); // This extent can be used directly
I am not making any claims on performance, but at least to the eye this seems to be more pleasing and elegant.
Trying out http://www.goxtk.com, great stuff!
Is there a quick way to get the bounding box for a model or some other point that could be used as the center of rotation of the camera? Worst case, what's the best way to loop over the points? Thanks for any replies!
It is possible to query each X.object() for its centroid, like this:
...
r = new X.renderer('r');
r.init();
o = new X.object();
o.load('test.vtk');
r.add(o);
r.render();
r.onShowtime = function() {
// print the centroid
console.log(o.points().centroid());
};
...
You have to overload the onShowtime function of the X.renderer to be sure that the X.object was properly setup (.vtk file loaded etc.).
To configure the camera, you can do f.e. the following:
...
r.camera().setPosition(-400,0,0); // set the position
r.camera().setFocus(-10,-10,-10); // set the focus point
r.camera().setUp(1,0,0); // set the (normalized) up vector
r.render();
...
Anyway, to loop over the points:
...
// o is an X.object
var numberOfPoints = o.points().count();
var pointArrayLength = o.points().length(); // equals numberOfPoints * 3
var allPoints = o.points().all(); // as a flat 1D array optimized for WebGL
// just loop it :)
...