Aligning topojson file with preset projection with points with point locations loaded via an api

260 views Asked by At

Thanks to a previous answer I was able to use geoproject to set the projection of my geojson to BC Albers.

    geoproject 'd3.geoAlbers().parallels([50, 58.5]).rotate([126, 0]).center([0,50.5]).fitSize([960, 600], d)'  < "$<" > "$@"

I'm then loading the topojson into d3 with a null projection

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

Everything looks fine when loaded, however when I then load point locations through an api and set the projection to match the topojson file

            var projection = d3.geoAlbers()
            .rotate([126,0])
            .center([0, 50.5])
            .parallels([50, 58.5])
            .scale(1500)
            .translate([960 / 2, 600 / 2]);

The points are not aligned with the background map map with points

My question is how do I line these up?

I want to be able to preset the projection using geoproject but the points loaded via an api need the projection set on the fly. I have a feeling it's to do with the .scale() in the on-the-fly projection vs the .fitSize() in the geoproject projection.

1

There are 1 answers

1
Andrew Reid On BEST ANSWER

You got it, the scale in your background feature was set with .fitSize, which (along with .fitExtent:

Sets the projection’s scale and translate to fit the specified GeoJSON object in the center of the given extent. (API doc).

So, while the scale and translate of the background data was set automatically, you are trying to manually scale (and translate) your unprojected data to match, without knowing the values set by .fitSize.

There are a couple possibilities to solve this:


First option, perhaps the easiest (despite appearing as a step backwards), is despite having already projected the data, you could use the unprojected data you started with and project it with the same projection that you use for the points. This allows you to easily modify both points and background features as you need in one step as all your features for display will be piped through the same projection function.


Second option is to find out the needed projection parameters. You know all these except scale and translate as you left that to the method .fitSize.

To get any projection parameters from a d3 projection, you can use the corresponding method without any arguments, so to get scale or translate use:

projection.scale();     // k
projection.translate(); // [x,y]

I'm not sure if you can achieve this from the command line, and you can't do it with the data you are projecting with a null projection (since it is already projected and does not need a d3 projection now). But you could source the scale and translate of the background by attempting to project the unprojected data in browser. Just set up the same projection you used on the command line and then get the scale and translate values (no need to actually draw anything):

var projection = d3.geoAlbers()
   .rotate([126,0])
   .center([0, 50.5])
   .parallels([50, 58.5])
   .fitSize([960,500],jsonFeature)

console.log(scale = projection.scale());
console.log(translate = projection.translate());

Then you have the missing two projection parameters needed to match both sets of data.

However, as I hinted above, if all features needing projection share the same coordinate system, life is easier when manipulating the map, so I would tend to favor option 1 (as you can change that layer's but not the point layer's coordinate system).