brennan
brennan

Reputation: 185

How to plot GeoJson data in d3.js

I am trying to display a floorplan to my webapp using d3.js and vue js. The data comes from a .json file and is in a geojson format. I generated some test data from this geojson website. I was thinking I could use the d3.js path function to plot each different object to a svg element. Would this be the correct way to go about doing this? I have looked at this tutorial to make a united states map from geojson data. I was thinking this would be the right way to do it if you inputted different data. However my program spits out random rectangles to the webapp. I think this may be becauseof the projection I am using but Im not sure. I havent used d3.js like this before so its all new to me. I included my code in below. Any help is greatly appreciated and im open to possibly using a different java script library but d3js is preferred.

createFloormap(){
      var svg = d3.select("svg")
      var width = +svg.attr("width")
      var height = +svg.attr("height")


      var path = d3.geoPath().projection(d3.geoAlbersUsa().scale(500))
      //var path = d3.geoPath()
      //var path = d3.geoPath().projection(d3.geoMercator().scale(100))



      var x = d3.scaleLinear()
          .domain([1, 10])
          .rangeRound([600, 860]);

      var color = d3.scaleThreshold()
          .domain(d3.range(2, 10))
          .range(d3.schemeBlues[9]);

      



    var promises = [
      d3.json("../static/data.json")
      //d3.json("https://raw.githubusercontent.com/adamjanes/udemy-d3/master/08/8.04/data/us-map.json")
    ]

    Promise.all(promises).then(function(data){
        ready(data[0]);
    })

    function ready(us) {
      console.log(us)
        svg.append("g")
            .selectAll("path")
              .data(us.features)
              //.data(topojson.feature(us, us.objects.states).features)
            .enter()
            .append("path")
                .attr("fill", "grey")
                .attr("d", path)
                .attr("stroke", "#fff")
                .attr("stroke-width", .2)
    }
    }
  },
{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "properties": {},
      "geometry": {
        "type": "Polygon",
        "coordinates": [
          [
            [
              -144.140625,
              45.583289756006316
            ],
            [
              -65.390625,
              45.583289756006316
            ],
            [
              -65.390625,
              69.28725695167886
            ],
            [
              -144.140625,
              69.28725695167886
            ],
            [
              -144.140625,
              45.583289756006316
            ]
          ]
        ]
      }
    },
    {
      "type": "Feature",
      "properties": {},
      "geometry": {
        "type": "Polygon",
        "coordinates": [
          [
            [
              9.4921875,
              39.36827914916014
            ],
            [
              159.609375,
              39.36827914916014
            ],
            [
              159.609375,
              70.02058730174062
            ],
            [
              9.4921875,
              70.02058730174062
            ],
            [
              9.4921875,
              39.36827914916014
            ]
          ]
        ]
      }
    }
  ]
}

the web app

Upvotes: 2

Views: 2312

Answers (1)

Andrew Reid
Andrew Reid

Reputation: 38171

My apologies, I mislead you when commenting on your last question. I read the code and saw an obvious issue - but missed the reference to a floor plan - which for the coordinates given was unlikely to be measured in latitude/longitude pairs. The answer below would be appropriate for a floor plan in lat/long pairs (as would be exported from geojson.io and because projection is less relevant at the building scale) or a geojson floor plan with coordinates in meters/feet.

Geojson in an arbitrary Cartesian coordinate system

You don't have geographic coordinates consisting of latitude/longitude pairs measured in degrees (as I thought when commenting), you have coordinates consisting of x,y values measured in some unit like metres or feet.

To project these corodinates we do not want to use d3.geoSomeProjection because these project latitude/longitude pairs on a sphere to a 2d plane. Nor do we want to use a null projection (the default projection for d3.geoPath) because that treats geojson coordinates as pixel coordinates (we can use a null projection when the coordinates in the geojson have been already been converted to pixel values - we know we don't want a null projection here because we have negative values).

Instead we can use d3.geoIdentity (the geo prefix indicates it is just part of the geo module of D3 but it doesn't require geographic coordinates). This "projection" allows us to apply some projection methods to the data, namely .center() or .scale(). D3 also has two convenience methods that set both simultaneously: fitExtent and fitSize which stretch and translate specified geojson to given dimensions:

var projection = d3.geoIdentity().fitSize([width,height],geoJsonObject)

var projection = d3.geoIdentity().fitExtent([[left,top],[right,bottom]],geoJsonObject)

So, with your data we get:

var data = {
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "properties": {},
      "geometry": {
        "type": "Polygon",
        "coordinates": [
          [
            [
              -144.140625,
              45.583289756006316
            ],
            [
              -65.390625,
              45.583289756006316
            ],
            [
              -65.390625,
              69.28725695167886
            ],
            [
              -144.140625,
              69.28725695167886
            ],
            [
              -144.140625,
              45.583289756006316
            ]
          ]
        ]
      }
    },
    {
      "type": "Feature",
      "properties": {},
      "geometry": {
        "type": "Polygon",
        "coordinates": [
          [
            [
              9.4921875,
              39.36827914916014
            ],
            [
              159.609375,
              39.36827914916014
            ],
            [
              159.609375,
              70.02058730174062
            ],
            [
              9.4921875,
              70.02058730174062
            ],
            [
              9.4921875,
              39.36827914916014
            ]
          ]
        ]
      }
    }
  ]
}

var width = 500;
var height = 300;
var svg = d3.select("svg")
  .attr("width",width)
  .attr("height",height);

var projection = d3.geoIdentity().fitSize([width,height],data)
var path = d3.geoPath(projection);

  
svg.selectAll("path")
  .data(data.features)
  .enter()
  .append("path")
  .attr("d",path);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg></svg>

One thing to note is that SVG coordinates space, to which d3.geoIdentity is treating coordinates as being in, has the opposite convention as might be expected: y=0 is at the top of the SVG with y values increasing as one moves down. If your coordinates appear to be upside down, then you can use d3.geoIdenity().fitSize(...).reflectY(true)

Upvotes: 1

Related Questions