Select part of an image

Asked

Viewed 390 times

3

Given the following image: Mapa

How can I javascript to select a country and paint it?

  • 4

    http://jqvmap.com/

3 answers

3

"How can I javascript to select a country and paint it?" (Raster images)

https://en.wikipedia.org/wiki/Flood_fill
https://en.wikipedia.org/wiki/Graph_traversal
https://en.wikipedia.org/wiki/Connected-component_labeling
http://www.algorytm.org/podstawy-grafiki/flood-fill/flood-fill-js.html (4 and 8 directions recursive implementation) (areas < ~40000 pixels)

'use strict';

let draw = async ( ) => {

const targetColor = 0xffffffff|0; // int32 rgba (cor branca).
let randomColor; // cor de preechimento.
const colorTolerance = 55; // 55 // 1 - 128 - 1020.
/*
let src = 'JirA9.gif'; // https://i.stack.imgur.com/JirA9.gif
*/
let src = URL.createObjectURL( new Blob( [ 
( await (await fetch( 'https://upload.wikimedia.org/wikipedia/commons/4/4d/BlankMap-World.svg' )).text() )
.replace( /#000000/g, '#ffffff' )
.replace( /#ffffff/g, '#99aaff' )
.replace( /#c0c0c0/g, '#ffffff' ) 
], { type:'image/svg+xml' } )
);

let canvas = document.getElementById( 'Mapa' );
let W, H, ctx;

let load  = (   ) => {
W = img.naturalWidth;
H = img.naturalHeight;
canvas = Object.assign( canvas, { 
style: `
transform: scale(${canvas.width / W}, ${canvas.height / H}); 
transform-origin: 0px 0px; 
`,
width:  W, 
height: H 
} );
ctx = canvas.getContext( '2d', { alpha: false } );
ctx.fillStyle = '#99AAFF'; // '#FFFFFF';
ctx.fillRect( 0, 0, W, H );
ctx.drawImage( img, 0, 0 );
}

let img = Object.assign( new Image( ), 
{ /*crossOrigin: 'anonymous',*/ src: src, /*onload: load*/ } );
img.addEventListener( 'load', load );

let rv = new Int32Array( 1000 );
crypto.getRandomValues( rv );
rv.sort();
//rv.sort((a, b) => b - a);
let it = 0;

let click = ( ev ) => {
let x = ev.offsetX * W / W|0;
let y = ev.offsetY * H / H|0;
randomColor = rv[ ++it ];
let imgData = ctx.getImageData( 0, 0, W, H );
let data = new Int32Array( imgData.data.buffer );
if( data[ ( y * W + x ) ] != targetColor ) return;
let pixToCheck = [ x, y ];
/*
while( pixToCheck[0] ){ // ===> Algoritmo I
y = pixToCheck.pop();
x = pixToCheck.pop();
data[ ( y * W + x ) ] == targetColor && ( data[ ( y * W + x ) ] = randomColor ) && pixToCheck.push( 
(x), (y + 1), // N
(x), (y - 1), // S
(x + 1), (y), // E
(x - 1), (y)  // O
);
}
*/
let color, colorDiff;
while( pixToCheck.length > 0 ){ // ===> Variante A
y = pixToCheck.pop();
x = pixToCheck.pop();
color = data[ ( y * W + x ) ];
if( !( color != randomColor ) ) continue;
colorDiff = (targetColor & 255) - (color & 255) + (targetColor >>>  8 & 255) - (color >>>  8 & 255) + (targetColor >>> 16 & 255) - (color >>> 16 & 255) + (targetColor >>> 24 & 255) - (color >>> 24 & 255);
if( !( colorDiff > -colorTolerance && colorDiff < colorTolerance ) ) continue;
data[ ( y * W + x ) ] = randomColor;
pixToCheck.push( 
(x + 1), (y), 
(x - 1), (y), 
(x), (y + 1), 
(x), (y - 1) 
);
}
ctx.putImageData( imgData, 0, 0 );
}

canvas.addEventListener( 'click', click );
}; draw()
<canvas title="click" id="Mapa" width="1000" height="508"></canvas>


Note: In the code above variant A and algorithm I can be used separately with the following differences:

inserir a descrição da imagem aqui

Result with https://i.stack.imgur.com/JirA9.gif

inserir a descrição da imagem aqui

SVG images without raster elements:

(async () => {

let Mapa = document.querySelector( '.Mapa' );
let srcSVG = new DOMParser()
.parseFromString( await (await fetch( 'https://upload.wikimedia.org/wikipedia/commons/4/4d/BlankMap-World.svg' )).text(), 'image/svg+xml' )
.querySelector( 'svg' );
srcSVG.style.transform = `scale(${Mapa.width.baseVal.value / srcSVG.width.baseVal.value}, ${Mapa.height.baseVal.value / srcSVG.height.baseVal.value})`;
srcSVG.style.transformOrigin = '0 0';
srcSVG.addEventListener( 'click', ( ev ) => {
if( ev.target.id != 'ocean' ) ev.target.style.fill = `rgba(
${ Math.random() * 256 & 255 }, /*R*/
    ${ Math.random() * 256 & 255 }, /*G*/
        ${ Math.random() * 256 & 255 }, /*B*/
            ${ 1.0 } /*A*/
            )`;
            }
);

Mapa.parentNode.replaceChild( srcSVG, Mapa );
})();
<svg xmlns="http://www.w3.org/2000/svg" class="Mapa" width="1000" height="508"></svg>

2

I imagine the most effective solution would be the use of SVG; I suggest you look for a map like this in svg, being represented in such a way that it resembles the example below:

<svg id="mapa-mundi">
  <path id="brasil" d="..." />
  <path id="usa" d="..." />
</svg>

And then you could freely manipulate each country using Jquery and coloring each country independently, based on user interaction...

<style>
  .colored{ fill:lightcoral }
</style>

$("#mapa-mundi > path").on("click",function() {
  $(this).toggleClass('colored');
});

0

Paint exactly the image I think is not possible, because it is an image . gif.

There are some other options, but laborious...

  1. play each country as a separate image, without background, placing them superimposed on an image with the blue background. " Cropping" and Positioning these images will be quite laborious. You can use GIMP to crop the images. For each record you must save a new image

    Naming files as -White. The important part would be the "-White", which should be exactly the same

  2. Create a copy of these colored images. Inserting them in the same position of the blank image, with style='display:None' to hide it, name them as "-Painted", the "-Painted" should be exactly the same.

  3. On each of the images, both white and color, put the following code:

    onclick="parents = this.src.split(-); this.src=(parents[1]=="White"? parents[0]+"-Painted":parents[0]+'-White')

You can test this with simple images before, like squares or something, before you risk having all the trouble of separating the images

Another option would be to use a vector image with , but that would also give you work.

The Vector maps option is the most appropriate if you don’t need this image exactly

Browser other questions tagged

You are not signed in. Login or sign up in order to post.