Coder Social home page Coder Social logo

d3-geo's Introduction

d3-geo

This module uses spherical GeoJSON to represent geographic features in JavaScript. D3 supports a wide variety of common and unusual map projections. And because D3 uses spherical geometry to represent data, you can apply any aspect to any projection by rotating geometry.

Resources

d3-geo's People

Contributors

5tefan avatar chrisuehlinger avatar curran avatar dechov avatar desmondcheongzx avatar fil avatar gregstoll avatar jkiss avatar jrus avatar lgrkvst avatar luissevillano avatar luizbarboza avatar martinfrances107 avatar mbostock avatar mfogel avatar nyurik avatar ondras avatar scotthmurray avatar snie2012 avatar stof avatar tomwanzek avatar tpreusse avatar tyrasd avatar veltman avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

d3-geo's Issues

Calling path.bounds breaks geoAlbersUsa.fitSize.

I think I've found an error dealing with projection.fitSize and path.bounds. Here's a block that reproduces it: https://bl.ocks.org/gabrielflorit/bb7ed6a56dbd5f66ee8c06b88a2b4f09

Specifically, accessing the bounds before calling fitSize makes the projection fit incorrectly:

// Filter US to lower 48 + AK + HI
var conus = topojson.feature(us, {
    type: 'GeometryCollection',
    geometries: us.objects.states.geometries.filter(function (d) { return d.id < 60; }),
})

// Create projection
var projection = d3.geoAlbersUsa()

// Create path
var path = d3.geoPath().projection(projection)

// Comment-in the following line and the projection gets fitted
// incorrectly.
var bounds = path.bounds(conus)

// Fit projection in half the container.
projection.fitSize([width/2, height/2], conus)

svg.append('path')
    .datum(conus)
    .attr('d', path)

screen shot 2016-09-20 at 3 31 57 pm

d3.geoPath.context() confuses Closure Compiler

The compiler complains that contextStream doesn't have pointRadius property.

PathString and PathContext should apparently be instantiated without new, since they return the created objects.

https://github.com/d3/d3-geo/blob/master/src/path/index.js

  path.context = function(_) {
    if (!arguments.length) return context;
    contextStream = (context = _) == null ? new PathString : new PathContext(_);
    if (typeof pointRadius !== "function") contextStream.pointRadius(pointRadius);
    return path;
  };

[geoPath()] Default Values Not Set

I am going over updating the TS definitions for d3-geo 1.3.

With the changes to the geoPath API, it seems that when using geoPath() or geoPath(someProjection) the defaults are not set internally for omitted arguments.

I.e. an omitted project and/or context, will return undefined when using the corresponding getter on the returned geo path generator.

As I am trying to breeze through the definitions update, you might be faster than me to create a quick PR for this repo. Otherwise, I will circle back to it asap. 😄

d3.geoFit{,Size,Extent}()

It’d be nice to have a simple projection that only does scale and translate (and possibly inverts y), so that you can use path.fitExtent or path.fitSize in conjunction with pre-projected TopoJSON.

You can resize manually with d3.geoTransform, but it’s a pain to compute the scale yourself.

add d3.geoNaturalEarth, please

why? because the D3/4 core build has no sane map projection for making a simple world map.
and no, plate carrée and mercator don't count.

let's just add geoNaturalEarth, or really like any other decent looking pseudo-cylindrical map projection.
not all of them, just one, so people can make better world maps out of the box.

thanks!

d3.geoPath, d3.geoProjection and basic projections

In the geoCircle PR discussion, you talked about pulling geoProjection and the rest of d3.v3.geo into d3-geo for d3 v4.0, and then breaking them out into d3-geo-projection for v5.0 when we do the new graphics pipeline.

I took a dive through the code, and found that geo.projection and geo.path have a circular dependency between them, and that geo.path has a dependency on the albers-usa projection (which then depends on other projections, etc.). It looks like the only way to migrate these over is to move geo.path, geo.projection and the basic projections over all at once (along with the clip-* and path-*` components). If this sounds like the right thing to do, I can get started on doing this all locally this week. (The eventual PR will have to wait for geoArea, geoCentroid and geoBounds to be merged)

Also, I've got a couple questions regarding the desire to eventually break d3.geoProjection into its own module. I understand the value of the d3-geo functions as tools for doing spherical math, but without projections, no one will be able to render maps. Do you see a lot of use cases where developers might import d3-geo and not need d3-geo-projection?

Don’t reassign listener methods?

I wonder if it would be faster to not reassign listener methods, since I expect that dynamically assigning methods makes it harder for the runtime to optimize.

d3.geoProjection

  • d3.geoProjection
  • projection.clipAngle
  • projection.clipExtent
  • rename projection.stream to projection.sink nah
  • remove sink.valid

See also #33 regarding projection.clipExtent.

d3.geoGraticule10()

It’s the same number of characters, but it might be a little more obvious to have a method that generates the default graticule geometry object directly, rather than constructing a default graticule generator, and then using it only once.

d3.geoGraticule10 = function() {
  return d3.geoGraticule()();
};

Graticule errors under gnomonic projection.

I've encountered some buggy behavior using the gnomonic projection with customized graticule steps. In some cases, there is incorrect line clipping. In others, an error is thrown. The issues arose when using a custom graticule step of [15, 15]. Removing the custom step or adjusting the projection clipAngle can make the issues to go away.

Below is an example that replicates the errors in d3 v4.2.1 and Chrome 51.0.2704.103 (Mac OS). The example assumes world-50m.json is on the relative path. Also, see comments below for more.

<!DOCTYPE html>
<meta charset="utf-8">
<style>

.graticule {
  fill: none;
  stroke: #777;
  stroke-width: .5px;
  stroke-opacity: .5;
}

.land {
  fill: #222;
}

.boundary {
  fill: none;
  stroke: #fff;
  stroke-width: .5px;
}

</style>
<body>
<script src="http://d3js.org/d3.v4.min.js"></script>
<script src="http://d3js.org/topojson.v1.min.js"></script>
<script>

var width = 960,
    height = 600;

var projection = d3.geoGnomonic()
    .scale(150)
    .translate([width / 2, height / 2])
    .rotate([-105, 0, 0]); // comment this line: no error, but bad line crossing
    // .clipAngle(90 - 1e-3); // uncomment this line, errors do not occur

var path = d3.geoPath()
    .projection(projection);

var graticule = d3.geoGraticule()
  .step([15, 15]); // comment this line, errors do not occur

var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height);

svg.append("path")
    .datum(graticule)
    .attr("class", "graticule")
    .attr("d", path);

d3.json("world-50m.json", function(error, world) {
  if (error) throw error;

  svg.insert("path", ".graticule")
      .datum(topojson.feature(world, world.objects.land))
      .attr("class", "land")
      .attr("d", path);

  svg.insert("path", ".graticule")
      .datum(topojson.mesh(world, world.objects.countries, function(a, b) { return a !== b; }))
      .attr("class", "boundary")
      .attr("d", path);
});

</script>

Projection tests.

It would be nice to have tests that take world-110m.json as input and verify compatibility with D3 3.x.

Add .contain() method?

Any interest in adding a projection.contain() method that projects to a bounding box a la http://bl.ocks.org/mbostock/4707858?

I think it's a pretty common use case to start with a bounding box rather than a desired scale/translate, and the alternative is to fiddle with values until it looks nice.

I took a crack at it, mostly seems to work except I need to figure out why it doesn't work for projections with clipExtents:

https://github.com/veltman/d3-geo/blob/master/src/projection/index.js#L81-100

In terms of naming, I chose contain because that's the CSS background-size nomenclature, but it could also be something like fitTo.

This would break the getter/setter pattern a bit. Alternatively, it could be a more consistent separate method like:

  var projection = ...;

  var fitter = d3.geoFit()
    .projection(projection)
    .width(1280)
    .height(720)
    .scale(0.9);

  // Change
  fitter.scale(1);

Although that seems kind of verbose. Any thoughts?

How to flip y with d3.geoIdentity()?

I'm trying to resize a preprojected geojson:

geoproject 'd3.geoIdentity().fitExtent([[0,0],[960,500]], d)'

… which leads to a vertically flipped output because in the original coordinate system, the origin is at the bottom left instead of the top. Using an extent of [[0,500],[960,0]] didn't help (that just rotated everything by 180°).

With the old topojson I would just do topojson --width=960 --height=500. Am I missing something?

Calling geoMercator.center sets the wrong automatic clipExtent.

In a few cases I've encountered geoPath fill errors, with country fills being inappropriately inverted.

Below is an example that replicates the errors in d3 v4.2.1 and Chrome 51.0.2704.103 (Mac OS). The example assumes world-50m.json is on the relative path. Also, see comments below for more.

<!DOCTYPE html>
<meta charset="utf-8">
<style>

.graticule {
  fill: none;
  stroke: #777;
  stroke-width: .5px;
  stroke-opacity: .5;
}

.land {
  fill: #222;
}

.boundary {
  fill: none;
  stroke: #fff;
  stroke-width: .5px;
}

</style>
<body>
<script src="http://d3js.org/d3.v4.min.js"></script>
<script src="http://d3js.org/topojson.v1.min.js"></script>
<script>

var width = 960,
    height = 500;

var projection = d3.geoMercator()
    .scale(150)
    .translate([width / 2, height / 2])
    .center([-1, 0]);

// This projection also exhibits fill errors
// when used with world-110m.json
// var projection = d3.geoAzimuthalEqualArea()
//     .scale(150)
//     .translate([width / 2, height / 2])
//     .rotate([0, 71, 0]);

var path = d3.geoPath()
    .projection(projection);

var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height);

d3.json("world-50m.json", function(error, world) {
  if (error) throw error;

  svg.insert("path", ".graticule")
      .datum(topojson.feature(world, world.objects.land))
      .attr("class", "land")
      .attr("d", path);

  svg.insert("path", ".graticule")
      .datum(topojson.mesh(world, world.objects.countries, function(a, b) { return a !== b; }))
      .attr("class", "boundary")
      .attr("d", path);
});

</script>

Sensible clipping defaults.

Some projections don’t behave well with the default antimeridian clipping. Even those that do sometimes have other default clip settings that make more sense—like orthographic should default to a 90° clip circle for the front hemisphere.

d3.geoBounds is not working

I stumbled upon an error while trying to use d3.geoBounds, and while trying to understand the cause of it I found something that is most likely a bug in area.js

Here's the part of the code I think is causing the error

export var areaRingSum;

var areaSum,
    lambda00,
    phi00,
    lambda0,
    cosPhi0,
    sinPhi0;

export var areaStream = {
  point: noop,
  lineStart: noop,
  lineEnd: noop,
  polygonStart: function() {
    areaRingSum.reset(); // <<< areaRingSum was never initialized
    areaStream.lineStart = areaRingStart;
    areaStream.lineEnd = areaRingEnd;
  },
  polygonEnd: function() {
    var areaRing = +areaRingSum;
    areaSum.add(areaRing < 0 ? tau + areaRing : areaRing);
    this.lineStart = this.lineEnd = this.point = noop;
  },
  sphere: function() {
    areaSum.add(tau);
  }
}

And here's the error

Cannot read property 'reset' of undefined
    at Object.areaStream.polygonStart (...\node_modules\d3-geo\build\d3-geo.js:169:18)
    at Object.boundsStream.polygonStart (...\node_modules\d3-geo\build\d3-geo.js:278:18)
    at streamPolygon (...\node_modules\d3-geo\build\d3-geo.js:143:12)
    at Object.streamGeometryType.MultiPolygon (...\node_modules\d3-geo\build\d3-geo.js:126:23)
    at streamGeometry (...\node_modules\d3-geo\build\d3-geo.js:88:40)
    at Object.streamObjectType.Feature (...node_modules\d3-geo\build\d3-geo.js:94:7)
    at geoStream (...\node_modules\d3-geo\build\d3-geo.js:150:36)
    at Object.bounds [as geoBounds] (...\node_modules\d3-geo\build\d3-geo.js:400:5)

I think it's worth noting that d3.geoCentroid and d3.geoLength all work on the same GeoJson object that causes the error when passed to d3.geoBounds

Also, d3.geoBounds returns [NaN, NaN] when a point is passed instead of a multipolygon, but I don't know the cause of this.

The division between d3-geo and d3-geo-projection

So at this point I've gone ahead and made speculative branches for the components that are listed in the issues (circle, area, centroid, bounds). I'm sure those will take some time to review, revise and merge, so in the meantime I've also looked ahead at the parts of d3.geo (v3) that are not included in the issues for this repo (or the scaffold of the README).

It seems to me that d3.geo.path, d3.geo.projection and the "batteries included" projections (like Albers) are being slated for the d3-geo-projection module (let me know if I've got any of this wrong). In v4 (or whenever it's ready) will d3-geo-projection become a first-class part of D3? Or is the intention to keep it as an optional/auxiliary module?

I forked d3-geo-projection just to take a look at its structure, but I'm holding off on getting to work until I better understand our goals.

circle.radius() is confusing

D3 4.0 renamed circle.angle to circle.radius. I'm having trouble understanding the concepts, so I hope someone can enlighten me.

The documentation currently says (emphasis mine):

circle.radius([radius])

If radius is specified, sets the circle radius to the specified angle in degrees, and returns this circle generator. The radius may also be specified as a function; this function will be invoked whenever a circle is generated, being passed any arguments passed to the circle generator. If radius is not specified, returns the current radius accessor, which defaults to:

In the source code, it appears the radius variable is passed into cos() and sin(), which has to be an angle.

Improper scoping in projections?

I'm trying to use the geoAlbersUsa projection, but this code returns an error.

Code:

import { geoAlbersUsa } from 'd3-geo';

const projection = geoAlbersUsa()
const coords = projection([37, -96]) || [];

Error:
d3-geo.js:2216 Uncaught TypeError: Cannot read property 'stream' of undefined

which is pointing to this line: https://github.com/d3/d3-geo/blob/master/src/projection/index.js#L13
and this file: https://github.com/d3/d3-geo/blob/a36f5cac465003ba8b6ceeee3834b1cef2b3a5f5/src/transform.js

I believe (though I could very well be using this incorrectly) that there's an issue with the function scoping, leading this to be undefined.

d3.geoClipExtent

Not a fan of this API. Maybe we can replace it by moving projection.clipExtent to path.clipExtent?

AlbersUSA comment

The AlbersUsa.js file says that the default scale is 1285 in the infile comments, but is actually 1070 in the code.

Projections

  • d3.geo.albers
  • d3.geo.albersUsa
  • d3.geo.azimuthalEqualArea
  • d3.geo.azimuthalEquidistant
  • d3.geo.conicConformal
  • d3.geo.conicEquidistant
  • d3.geo.conicEqualArea
  • d3.geo.equirectangular
  • d3.geo.gnomonic
  • d3.geo.mercator
  • d3.geo.orthographic
  • d3.geo.stereographic
  • d3.geo.transverseMercator

Missing method?

Apologies in advance if I've misunderstood something or logged this issue in the wrong place!

I've built numerous pan-and-zoom maps before, all of which used either the mercator or albers projections (I should also point out at this point that I'm using v3, not v4, of d3): https://github.com/d3/d3-3.x-api-reference/blob/master/Geo-Projections.md

I'm now trying to build a US map, and switched to the albersUsa projection. All of a sudden, my map code is giving me lots of errors, and it suggests that there is a missing method in the albersUsa projection?

this.projection = d3.geo.albers() // `albers` is ok, `albersUsa` is not!
            .scale(this.mapModel.get('mapScale'))
            .translate(this.mapModel.get('translate'));

// ...a while later:

let bounds = [0,0]; // just for this example
const coords = this.projection(bounds);

In the albers projection, coords comes out as [2065.4195284613525, 73.63503703113963]. But if I switch to albersUsa, coords comes out as null.

Any advice would be much appreciated! Thanks

d3.geoPath

  • d3.geoPath
  • path.area
  • path.bounds
  • path.centroid

[geoPath]: pointRadius() Accessor pattern

This is more of a question. I noticed that the geo path pointRadius() getter returns:

  1. either an accessor function, or
  2. a constant numeric value (i.p. the 4.5 default)

If I recall correctly, in a fair share of the other D3 modules, similar accessors will always return an accessor function, even if it just wraps a constant return value.

Understanding that changing this would be breaking, I simply wondered, if there is a rationale for it, which is specific to the d3-geo applications?

d3-geo not working in React

I was trying to implement a bubble map chart in React, using the example of Mercator projection. However, I got errors about cannot find geoMercator all the time. I imported d3 by npm but got the error of function missing. I tried d3-geo, and d3-geo-projection but none of them works. As a newbie of React any help is appreciated!
Error:

WorldMap.js:61 Uncaught (in promise) TypeError: _d2.default.geoMercator is not a function

Code:
`import $ from 'jquery';
import ReactFauxDOM from 'react-faux-dom';
import firebase from 'firebase';
import React from 'react';
import d3 from 'd3';
import d3geo from 'd3-geo-projection';
import topojson from 'topojson';

class WorldMap extends React.Component {
constructor(props) {
super()
}

render(){

    var width = 960,
    height = 600;

var projection = d3.geoMercator()
                    .scale((width - 3) / (2 * Math.PI))
                    .translate([width / 2, height / 2]);

var path = d3.geoPath()
    .projection(projection);

var graticule = d3.geoGraticule();

var svgNode = ReactFauxDOM.createElement('div');

var svg = d3.select(svgNode).append("svg")
    .attr("width", width)
    .attr("height", height);
svg.append("defs").append("path")
    .datum({ type: "Sphere" })
    .attr("id", "sphere")
    .attr("d", path);

svg.append("use")
    .attr("class", "stroke")
    .attr("xlink:href", "#sphere");

svg.append("use")
    .attr("class", "fill")
    .attr("xlink:href", "#sphere");
svg.append("path")
    .datum(graticule)
    .attr("class", "graticule")
    .attr("d", path);

d3.json("./maptest.json", function(error, world) {
    if (error) throw error;

    svg.insert("path", ".graticule")
        .datum(topojson.feature(world, world.objects.land))
        .attr("class", "land")
        .attr("d", path)
        .style("fill", "gray");

    svg.insert("path", ".graticule")
        .datum(topojson.mesh(world, world.objects.countries, function(a, b) {
            return a !== b; }))
        .attr("class", "boundary")
        .attr("d", path)
        .style("fill", "gray");
});

svg.selectAll("circle")
        .data(this.cases)
        .enter().append("circle", ".pin")
        .attr("r", 2)
        .attr("transform", function(d) {
            return "translate(" + projection([
                d.longitude,
                d.latitude
            ]) + ")";
        })
        .style("fill", "steelblue");

return svgNode.toReact();

}

}

export default WorldMap;

`

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.