Utilizing D3 to fetch geographic data in the form of a TopoJSON file for U.S. counties

After retrieving a set of coordinates, the goal is to use D3 to find the corresponding county from a U.S. TopoJSON file. Here is an example code snippet:

navigator.geolocation.getCurrentPosition(function(position) {
  let coordinates: [number, number] = [position.coords.longitude, position.coords.latitude]
}

// e.g. [-70.1234567, 40.987654]

The function to retrieve county information using D3 is as follows:

getGeoLocation(topology, coordinates) {
  topojson.feature(topology, topology["objects"]["counties"])["features"].filter(function(polygon) {
    polygon["geometry"]["coordinates"].forEach(coord => {
      console.log(d3.polygonContains(coord, coordinates))
    })
  })
}

Despite implementing this function, it consistently returns false for every county object. There seems to be some confusion whether to use d3.geoContains() or d3.polygonContains(), especially when dealing with MultiPolygon features in the provided TopoJSON file (link here: ).

Any suggestions on how to address this issue?

UPDATE

Could the problem lie in the lack of projection within the geoPath() used on the TopoJSON data? Even without projection, the comparison with basic JSON does not resolve the issue...

Provided below is some additional context about the map (developed in Angular with TypeScript), where this.topology refers to the imported JSON from the specified URL:

path: any = d3.geoPath()

this.svg = d3.select("#map")
  .attr("preserveAspectRatio", "xMidYMid meet")
  .attr("viewBox", "0 0 975 610")
  .attr("height", "100%")
  .attr("width", "100%")

this.g = this.svg.append("g")
  .attr("id", "g")

this.counties = this.g.append("g")
  .selectAll("path")
  .data(topojson.feature(this.topology, this.topology["objects"]["counties"])["features"])
  .join("path")
  .attr("d", this.path)
  .attr("class", "county")
  .attr("id", function(d) {return d["id"]})

Answer №1

d3.geoContains specifically operates on spherical coordinates (WGS84) indicated by longitude and latitude in degrees. The TopoJSON utilized originates from this specific source and has already undergone projection to a pixel range through the Albers projection. Hence, using them together is incompatible.

The resolution lies in employing a pathContains method (with an existing issue raised concerning this). Unlike geoContains, which executes polygonContains on an altered set of sphere-based coordinates, planar calculations suffice given the pre-projection of geometries. Naturally, input point coordinates should also undergo projection.

The provided code snippet should serve the purpose, although it may not preempt all potential obstacles that could arise.

const projection = d3.geoAlbersUsa().scale(1300).translate([487.5, 305]);
const geo_path = d3.geoPath();

function pathContains(feature, point) {
  const coordinates = feature.geometry.coordinates;
  const n = coordinates.length;
  if (!!point) {
    for (let i = 0; i < n; i++) {
      if (d3.polygonContains(coordinates[i][0], point)) return true;
    }
  }
  return false;
}

d3.json("https://cdn.jsdelivr.net/npm/us-atlas@3/counties-albers-10m.json")
  .then(json => {
    const geo_shapes = topojson.feature(json, json.objects.states);
    function render() {
      const point = [
        d3.select("#longitude").property("value"),
        d3.select("#latitude").property("value"),
      ]
      const projected = projection(point);
      d3.select("#map")
        .selectAll("path")
        .data(geo_shapes.features, d => d.id)
        .join("path")
          .attr("d", geo_path)
          .attr("class", "map-feature")
          .attr("fill", d => pathContains(d, projected) ? "red" : "white")
          .attr("stroke", "gray");

      d3.select("circle")
        .raise()
        .attr("cx", !projected ? 0 : projected[0])
        .attr("cy", !projected ? 0 : projected[1])
        .attr("r", !projected ? 0 : 3);
    }
    d3.selectAll("input").on("input", render);
    d3.select("#map")
      .append("circle");
    render();
  });
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.3.0/d3.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/topojson/3.0.2/topojson.min.js"></script>
<input type="number" multiple="0.1" id="longitude" value="-96.6">
<input type="number" multiple="0.1" id="latitude" value="38.6">
<svg id="map" viewBox="0 0 975 610"></svg>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Generate a customizable button in Bootstrap using JavaScript extensions

After some investigation on Bugzilla, I discovered that by setting the options URL in install.rdf to arbitrary JavaScript, it can run smoothly. However, a strange issue arises where the window deactivates, as if an invisible dialog has appeared over it, ma ...

Error: AppModule requires an array of arguments in order to function properly

Upon successfully compiling my Angular application and running ng serve, I encountered the following error in the browser console. AppComponent_Host.ngfactory.js? [sm]:1 ERROR Error: Arguments array must have arguments. at injectArgs (core.js:1412) at c ...

What is the best way to change the "MuiPaper-elevation1" attribute in a Card element within Material-UI?

My Card component in HTML looks like this: <div class="MuiPaper-root MuiCard-root makeStyles-Card-5 MuiPaper-elevation1 MuiPaper-rounded"> I want to change MuiPaper-elevation1 to MuiPaper-elevation0 to remove the shadow. I attempted: ...

Autocomplete failing to provide a valid response, returning a null value instead

Utilizing an Autocomplete feature for employee search, users can input a name and select from the list of results. However, the current onChange function logs the index value instead of the selected employee's name. Is there a way to pass the employee ...

Detecting mistakes using ES6 assurances and BookshelfJS

I'm working on implementing a simple login method for a Bookshelf User model in an ExpressJS application. However, I am facing issues with handling errors from the rejected promises returned by the login function in the User model. While referring to ...

Move the accordion slider to the top of the browser

I'm working with an accordion menu that has some long content. To make it more user-friendly, I want to add a slide effect when the accordion items are opened. Right now, if you open the first two menu items, the last item's content is cut off a ...

Ways to contact a .aspx method from a .cs file

My web page includes JavaScript in the .aspx file for a save button. The source code declares a function called OnClientClick="javascript: validateTextTest()", and this function is called in the head of the source code using validateTextTest(). Here is th ...

Can a single camera be utilized for capturing two different scenes?

Hey there! I'm currently working on rendering two different scenes with a camera that moves in sync between the two. Here's what I'm trying to accomplish: I need to display two separate point clouds in two distinct scenes, but I want the ca ...

Run JavaScript code last before page unloads

Two pages are involved in this process. On the first page, there is a form that the user completes and then clicks on the submit button. After that, the code for the first page unloads, and the user is redirected to the second page. Actual Issue A loadin ...

Retrieve JSON data from an external link and showcase it within a div, unfortunately encountering an issue with the functionality

Encountering the error message "XMLHttpRequest cannot load . No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin '' is therefore not allowed access" Check out the plunker link for more information: http ...

Click on a specific button within a DataTable row

I am working with a dataTable that fetches data from the database using Jquery. In each row, there are two buttons for accepting or rejecting something. My goal is to make the accept button disappear when rejecting and vice versa. public function displayT ...

Transform an array into an array of objects using the reduce method

optimizedRoute = ['Bengaluru', 'Salem', 'Erode', 'Tiruppur', 'Coimbatore'] result = [ {start: bengaluru, end: salem}, {start: salem, end: erode}, {start: erode, end: tiruppur}, {start: tiruppur, en ...

Tips for keeping the header section anchored to the top of the page

I am having trouble getting the menu bar and logo to stick at the top of my header section in the template. I have been trying different solutions, including using the sticky.js plugin for almost 4 days now but it's not working. I also have parallax e ...

The ".splice()" method continuously removes the final element from an array

I have implemented a function on my form that allows me to add multiple file inputs for various images by clicking a button. Although this functionality is working correctly, I am facing an issue while trying to delete an input field using .splice. Instead ...

Executing JavaScript code within ASP.NET Visual Basic

My current web application uses jQuery and JavaScript, but I want to add more features that are supported in ASP.net VB. However, I am unsure if the JavaScript can run after the VB code. Essentially, I would like the web app to follow this sequence: ...

Importing an array of Vue components to be exported and utilized in the app.js file

I'm currently working on a Laravel 8 project with vue.js v2.6 and I want to clean up my app.js file by moving all of my Vue.component() declarations to a separate file. To achieve this, I created js/vueComponents.js where I placed all the vue componen ...

Setting up "connect-redis" in a TypeScript environment is a straightforward process

Currently, I am diving into the Fullstack React GraphQL TypeScript Tutorial I encountered an issue while trying to connect Redis with express-session... import connectRedis from "connect-redis"; import session from "express-session"; ...

Step-by-step guide on retrieving the button text by utilizing a method call

Currently, I am troubleshooting a demo and I'm puzzled as to why the text of the #add-point button is not displaying. $("#add-point").on("click", function(){ activatePointTool(); }); function activatePointTool() { var tool = $(this).text().toU ...

I was confused about the distinction between the https.get() and https.request() functions in the Node.js npm package for https

// # Exciting Nodejs Programs! const https = require('https'); https.get('https://www.google.com/', (res) => { console.log('statusCode:', res.statusCode); console.log('headers:', res.headers); res.on ...

Angular 2: The linting error shows up as "Anticipated operands need to be of the same type or any"

So, I have this shared service file where a variable is defined like so: export class SharedService { activeModal: String; } Then, in my component file, I import the service and define it as follows: constructor(public sharedService: SharedService) ...