# Project MViz - Update 3

How to do picking (i.e. country selection on mouseclick)?

I came across this project by Steve Hall. In it he makes a 3d map using Three.js and D3 that is responsive to mouseover and click events. He also has a great writeup that shares how he tackled the problem.

At the time, I didn’t really understand what the article was saying. But two things I did takeaway were,

1. the use of Three.js’s built-in raycasting to figure out the mouse’s coordinates in 3d space
2. the use of an algorithm to determine if a point is inside a bounded region

2D Picking

A bit of searching later, I came across the related Wikipedia article (Point in Polygon). Even luckier, I came across this Stack Overflow answer which mentions that HTML Canvas already has a built-in, optimized algorithm (`isPointInPath()`) for doing this detection. Yay!

With this in mind, I reworked my map drawing code a bit. Specifically, I saved each continent’s path as a Path2D object. And on mousemove, simply called `isPointInPath( continentPath )`.

Here is a demo. (Mouseover the continents to see their names.)

And here’s the code,

``````
// Initialize
for( var j = 0; j < rawishCoords.length; j++ ){

// Use Path2D() to store path data for later access, stackoverflow.com/a/28913470
var country = new Path2D( rawishCoords[j].d );

// draw the path
ctx.strokeStyle = "blue";
ctx.stroke(country);

rawishCoords[j].canvasPath = country;
}

// Listen for mousemove

var mousePos = getMousePos(c, evt);
whereIsThisPoint( mousePos.x, mousePos.y );

}, false);

// Check if point in path
var whereIsThisPoint = function( px, py ){
// Use canvas's built-in isPointInPath() method
//  developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/isPointInPath

for( var i = 0; i < rawishCoords.length; i++ ){

var pointInPath = ctx.isPointInPath( rawishCoords[i].canvasPath, px, py);

if ( pointInPath ){
console.log( rawishCoords[i].id );
document.getElementById("textDiv").textContent = rawishCoords[i].id;
}
}
};
``````

3D Picking

As for the 3D raycasting, I used one of Lee Stemkoski’s excellent Three.js demos, in combination with the official docs to get it working.

The code for this is as shown in the docs,

``````
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();

function onMouseMove( event ) {

// calculate mouse position in normalized device coordinates
// (-1 to +1) for both components

mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
}

function render() {

// update the picking ray with the camera and mouse position
raycaster.setFromCamera( mouse, camera );

// calculate objects intersecting the picking ray
var intersects = raycaster.intersectObjects( scene.children );

for ( var i = 0; i < intersects.length; i++ ) {

intersects[ i ].object.material.color.set( 0xff0000 );
}

renderer.render( scene, camera );
}

The idea, would be to convert the 3D mouse coordinates to 2D coordinates, and use those with the `isPointInPath()` method.