A 'little' bit of number crunching can define areas or shapes of particular colors in an image. E.G. Starting with this:
Original image, cropped and reduced to a binary (2 color) image, then the oceans flood filled. All but the flood filling done in Java code not shown.
Then running the shape algorithm based on colors near white (be patient - it takes a while), will produce a series of areas used to render this over the top. A mouse motion listener has been added to color the area under the pointer to dark green.

This is the code that produces that image. The mouse pointer is currently pointing at China.
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
import java.net.URL;
import java.util.ArrayList;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.imageio.ImageIO;
/*
Outline code from:
https://stackoverflow.com/q/7218309/418556
*/
public class WorldMapArea {
private JComponent ui = null;
JLabel output = new JLabel();
public static final int SIZE = 750;
BufferedImage image;
Area area;
ArrayList<Shape> shapeList;
public WorldMapArea() {
try {
initUI();
} catch (Exception ex) {
ex.printStackTrace();
}
}
public final void initUI() throws Exception {
if (ui != null) {
return;
}
URL url = new URL("https://i.stack.imgur.com/N4eOn.png");
image = ImageIO.read(url);
long then = System.currentTimeMillis();
System.out.println("" + then);
area = getOutline(Color.WHITE, image, 12);
long now = System.currentTimeMillis();
System.out.println("Time in mins: " + (now - then) / 60000d);
shapeList = separateShapeIntoRegions(area);
ui = new JPanel(new BorderLayout(4, 4));
ui.setBorder(new EmptyBorder(4, 4, 4, 4));
output.addMouseMotionListener(new MousePositionListener());
ui.add(output);
refresh();
}
public Area getOutline(Color target, BufferedImage bi, int tolerance) {
// construct the GeneralPath
GeneralPath gp = new GeneralPath();
boolean cont = false;
for (int xx = 0; xx < bi.getWidth(); xx++) {
for (int yy = 0; yy < bi.getHeight(); yy++) {
if (isIncluded(new Color(bi.getRGB(xx, yy)), target, tolerance)) {
//if (bi.getRGB(xx,yy)==targetRGB) {
if (cont) {
gp.lineTo(xx, yy);
gp.lineTo(xx, yy + 1);
gp.lineTo(xx + 1, yy + 1);
gp.lineTo(xx + 1, yy);
gp.lineTo(xx, yy);
} else {
gp.moveTo(xx, yy);
}
cont = true;
} else {
cont = false;
}
}
cont = false;
}
gp.closePath();
// construct the Area from the GP & return it
return new Area(gp);
}
public static ArrayList<Shape> separateShapeIntoRegions(Shape shape) {
ArrayList<Shape> regions = new ArrayList<>();
PathIterator pi = shape.getPathIterator(null);
GeneralPath gp = new GeneralPath();
while (!pi.isDone()) {
double[] coords = new double[6];
int pathSegmentType = pi.currentSegment(coords);
int windingRule = pi.getWindingRule();
gp.setWindingRule(windingRule);
if (pathSegmentType == PathIterator.SEG_MOVETO) {
gp = new GeneralPath();
gp.setWindingRule(windingRule);
gp.moveTo(coords[0], coords[1]);
} else if (pathSegmentType == PathIterator.SEG_LINETO) {
gp.lineTo(coords[0], coords[1]);
} else if (pathSegmentType == PathIterator.SEG_QUADTO) {
gp.quadTo(coords[0], coords[1], coords[2], coords[3]);
} else if (pathSegmentType == PathIterator.SEG_CUBICTO) {
gp.curveTo(
coords[0], coords[1],
coords[2], coords[3],
coords[4], coords[5]);
} else if (pathSegmentType == PathIterator.SEG_CLOSE) {
gp.closePath();
regions.add(new Area(gp));
} else {
System.err.println("Unexpected value! " + pathSegmentType);
}
pi.next();
}
return regions;
}
class MousePositionListener implements MouseMotionListener {
@Override
public void mouseDragged(MouseEvent e) {
// do nothing
}
@Override
public void mouseMoved(MouseEvent e) {
refresh();
}
}
public static boolean isIncluded(Color target, Color pixel, int tolerance) {
int rT = target.getRed();
int gT = target.getGreen();
int bT = target.getBlue();
int rP = pixel.getRed();
int gP = pixel.getGreen();
int bP = pixel.getBlue();
return ((rP - tolerance <= rT) && (rT <= rP + tolerance)
&& (gP - tolerance <= gT) && (gT <= gP + tolerance)
&& (bP - tolerance <= bT) && (bT <= bP + tolerance));
}
private void refresh() {
output.setIcon(new ImageIcon(getImage()));
}
private BufferedImage getImage() {
BufferedImage bi = new BufferedImage(
2 * SIZE, SIZE, BufferedImage.TYPE_INT_RGB);
Graphics2D g = bi.createGraphics();
g.drawImage(image, 0, 0, output);
g.setColor(Color.ORANGE.darker());
g.fill(area);
g.setColor(Color.RED);
g.draw(area);
try {
Point p = MouseInfo.getPointerInfo().getLocation();
Point p1 = output.getLocationOnScreen();
int x = p.x - p1.x;
int y = p.y - p1.y;
Point pointOnImage = new Point(x, y);
for (Shape shape : shapeList) {
if (shape.contains(pointOnImage)) {
g.setColor(Color.GREEN.darker());
g.fill(shape);
break;
}
}
} catch (Exception doNothing) {
}
g.dispose();
return bi;
}
public JComponent getUI() {
return ui;
}
public static void main(String[] args) {
Runnable r = () -> {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception ex) {
ex.printStackTrace();
}
WorldMapArea o = new WorldMapArea();
JFrame f = new JFrame(o.getClass().getSimpleName());
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
f.setLocationByPlatform(true);
f.setContentPane(o.getUI());
f.setResizable(false);
f.pack();
f.setVisible(true);
};
SwingUtilities.invokeLater(r);
}
}