Show country names in a p5 worldmap

Hi there,

I need to show the country name when we have the pointer over a country position on a world map. Could you guys please help me? Don’t even know from where to start with this.

The code is in this codepen:

Thanks!

1 Like
  1. In setup, or when your code loads, pre-convert your path style polygon notation with an array of coordinates.
  2. Implement hit-testing for polygons (classic point in polygon problem): Point in polygon - Wikipedia
  3. [Optional] Optimize by computing bounding boxes for each polygon, and check that before applying the point in polygon algorithm.
1 Like

Here is a sample implementation of the ray casting points in polygon algorithm: p5.js Web Editor

2 Likes

Hi Paul, thanks for your response.

I have tried using the method from your p5.js Web Editor, but unfortunately I don’t seen to be able to reproduce it. Any chances you have more content about this or could just show from that code pen?

Thanks!

I’d like to see what you tried. That way I can point out any mistakes or try to clarify the specific aspects that weren’t clear. Otherwise I’ll just feel like I’m doing your homework for you :sweat_smile:.

1 Like

@KumuPaul, I moved it here - p5.js Web Editor

Only tried to color when the mouse is over a coord_point, but this doesn’t seen to work. I bet I’m doing something very silly :confused:

Did I misunderstand your code? pointInPoly seems to take the vertex coordinates and the mouseX & Y.

  1. You need to check for intersection and set the fill before you start drawing the country shapes
  2. pointInPoly expects a list of vertices that describe the polygon, not a single vertex. So you’ll need to check it against each polygon that makes up each country (see code below).
  3. Your country shapes are defined in a sort of SVG path notation with “move” commands and relative coordinates. For this algorithm to work you need to convert this notation to a list of vertices (actually a list of lists because some countries are made up of multiple polygons) (see code below).
  4. My code was using vectors (or a vector-like object with x and y properties), but your code represents coordinates as a 2-Tuple (an array of length 2 containing the x value and y value in order). So you’ll need to update pointInPoly to use [0] and [1] to access the coordinates instead of .x and .y

Checking if the mouse is over any one of a list of polygons:

    // Note country[i].polygons is hypothetical, assuming you add code to pre-convert each country path into a list of polygons and store it on the country object.
    if (country[i].polygons.some(poly => pointInPoly(poly, createVector(mouseX, mouseY)))) {
      fill('red');
    } else {
      fill('gray');
    }

Converting SVG paths to polygons:

// This is very similar to your existing drawing code, but instead of actually drawing with beginShape/vertex/endShape this pushes vertices into an array, and then starts a new array each time it sees the "z" command.
function convertPathToPolygons(path) {
  let coord_point = [0, 0];
  let polygons = [];
  let currentPolygon = [];

  //For loop para calcular os pontos do vertex
  for (const node of path) {
    if (node[0] == "m") {
      coord_point[0] += node[1] * size;
      coord_point[1] += node[2] * size;
      currentPolygon = [];
    } else if (node[0] == "M") {
      coord_point[0] = node[1] * size;
      coord_point[1] = node[2] * size;
      currentPolygon = [];
    } else if (node == "z") {
      currentPolygon.push([...coord_point]);
      polygons.push(currentPolygon);
    } else {
      currentPolygon.push([...coord_point]);
      coord_point[0] += node[0] * size;
      coord_point[1] += node[1] * size;
    }
  }
  
  return polygons;
}
2 Likes

Thanks again @KumuPaul, I feel like this is all far beyond my knowledge :cry:

// Note country[i].polygons is hypothetical, assuming you add code to pre-convert each country path into a list of polygons and store it on the country object.

When you mentioned that, how do I store something in the object after getting the values?

For the rest of the part I feel like most of it is wrong. p5.js Web Editor
For instance, when I console log polygons it returns the same arrays multiple times again and again. Is this meant to be happening?

Because JavaScript is a dynamically typed language you can just add new properties to existing objects. You don’t want to redo the SVG Path → Polygon conversion every frame, so it is best to put this in setup():


  for (var i = 0; i < country.length; i++) {
    country[i].polygons = convertPathToPolygons(country[i].vertexPoint);
    
    console.log(country[i].name);
    console.log(country[i].polygons);
  }

Also there was what looked like a copy paste issue in the convertPathToPolygons function:

      // In both lines where a vertex is added to the current polygon array your code was missing the spread operator (...)
      currentPolygon.push([...coord_point]);

The spread operator ... takes an array and expands it, it can be used both in the context of an array literal, or passing arguments to a function. In this case I’m using it to make a copy of coord_point. You could also use coord_point.slice() if you prefer not to use the spread operator, which is a relatively new language feature.

1 Like

Here’s the working version: p5.js Web Editor

I’ve just noticed a bug: countries which are enclaves (completely surrounded by another country, such as Lesotho within South Africa, cause the surrounding country to be highlighted as well. It should be possible to fix this by considering all of the polygons for a given country. If the mouse is inside of an even number of the polygons then it is actually outside the country (there is one polygon for the outside perimeter and one polygon for the hole).

2 Likes

I thought the spread operator ... was a typo :zipper_mouth_face: thanks for letting me know! Never seen this syntax before.

I have created a new for loop to print the country when the mouse is over the country and it works perfectly! p5.js Web Editor

I do see the bug that you mentioned, will see if I can get anywhere with this, but this is by far better than I expected because after multiple attempts I have gone for drawing the map twice, once coloured and another time not and used p5.js get to print the country names, but it seemed like a very wrong approach.

Thank you for everything! This is incredible! Hopefully one day I will be able to do these things. Thanks!

1 Like