123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311 |
- 'use strict';
- /* global shapefile */
- /* eslint no-console: off */
- /* eslint no-unused-vars: off */
- async function main() {
- const size = 4096;
- const pickCtx = document.querySelector( '#pick' ).getContext( '2d' );
- pickCtx.canvas.width = size;
- pickCtx.canvas.height = size;
- const outlineCtx = document.querySelector( '#outline' ).getContext( '2d' );
- outlineCtx.canvas.width = size;
- outlineCtx.canvas.height = size;
- outlineCtx.translate( outlineCtx.canvas.width / 2, outlineCtx.canvas.height / 2 );
- outlineCtx.scale( outlineCtx.canvas.width / 360, outlineCtx.canvas.height / - 180 );
- outlineCtx.strokeStyle = '#FFF';
- const workCtx = document.createElement( 'canvas' ).getContext( '2d' );
- workCtx.canvas.width = size;
- workCtx.canvas.height = size;
- let id = 1;
- const countryData = {};
- const countriesById = [];
- let min;
- let max;
- function resetMinMax() {
- min = [ 10000, 10000 ];
- max = [ - 10000, - 10000 ];
- }
- function minMax( p ) {
- min[ 0 ] = Math.min( min[ 0 ], p[ 0 ] );
- min[ 1 ] = Math.min( min[ 1 ], p[ 1 ] );
- max[ 0 ] = Math.max( max[ 0 ], p[ 0 ] );
- max[ 1 ] = Math.max( max[ 1 ], p[ 1 ] );
- }
- const geoHandlers = {
- 'MultiPolygon': multiPolygonArea,
- 'Polygon': polygonArea,
- };
- function multiPolygonArea( ctx, geo, drawFn ) {
- const { coordinates } = geo;
- for ( const polygon of coordinates ) {
- ctx.beginPath();
- for ( const ring of polygon ) {
- ring.forEach( minMax );
- ctx.moveTo( ...ring[ 0 ] );
- for ( let i = 0; i < ring.length; ++ i ) {
- ctx.lineTo( ...ring[ i ] );
- }
- ctx.closePath();
- }
- drawFn( ctx );
- }
- }
- function polygonArea( ctx, geo, drawFn ) {
- const { coordinates } = geo;
- ctx.beginPath();
- for ( const ring of coordinates ) {
- ring.forEach( minMax );
- ctx.moveTo( ...ring[ 0 ] );
- for ( let i = 0; i < ring.length; ++ i ) {
- ctx.lineTo( ...ring[ i ] );
- }
- ctx.closePath();
- }
- drawFn( ctx );
- }
- function fill( ctx ) {
- ctx.fill( 'evenodd' );
- }
- // function stroke(ctx) {
- // ctx.save();
- // ctx.setTransform(1, 0, 0, 1, 0, 0);
- // ctx.stroke();
- // ctx.restore();
- // }
- function draw( area ) {
- const { properties, geometry } = area;
- const { type } = geometry;
- const name = properties.NAME;
- console.log( name );
- if ( ! countryData[ name ] ) {
- const r = ( id >> 0 ) & 0xFF;
- const g = ( id >> 8 ) & 0xFF;
- const b = ( id >> 16 ) & 0xFF;
- countryData[ name ] = {
- color: [ r, g, b ],
- id: id ++,
- };
- countriesById.push( { name } );
- }
- const countryInfo = countriesById[ countryData[ name ].id - 1 ];
- const handler = geoHandlers[ type ];
- if ( ! handler ) {
- throw new Error( 'unknown geometry type.' );
- }
- resetMinMax();
- workCtx.save();
- workCtx.clearRect( 0, 0, workCtx.canvas.width, workCtx.canvas.height );
- workCtx.fillStyle = '#000';
- workCtx.strokeStyle = '#000';
- workCtx.translate( workCtx.canvas.width / 2, workCtx.canvas.height / 2 );
- workCtx.scale( workCtx.canvas.width / 360, workCtx.canvas.height / - 180 );
- handler( workCtx, geometry, fill );
- workCtx.restore();
- countryInfo.min = min;
- countryInfo.max = max;
- countryInfo.area = properties.AREA;
- countryInfo.lat = properties.LAT;
- countryInfo.lon = properties.LON;
- countryInfo.population = {
- '2005': properties.POP2005,
- };
- //
- const left = Math.floor( ( min[ 0 ] + 180 ) * workCtx.canvas.width / 360 );
- const bottom = Math.floor( ( - min[ 1 ] + 90 ) * workCtx.canvas.height / 180 );
- const right = Math.ceil( ( max[ 0 ] + 180 ) * workCtx.canvas.width / 360 );
- const top = Math.ceil( ( - max[ 1 ] + 90 ) * workCtx.canvas.height / 180 );
- const width = right - left + 1;
- const height = Math.max( 1, bottom - top + 1 );
- const color = countryData[ name ].color;
- const src = workCtx.getImageData( left, top, width, height );
- for ( let y = 0; y < height; ++ y ) {
- for ( let x = 0; x < width; ++ x ) {
- const off = ( y * width + x ) * 4;
- if ( src.data[ off + 3 ] ) {
- src.data[ off + 0 ] = color[ 0 ];
- src.data[ off + 1 ] = color[ 1 ];
- src.data[ off + 2 ] = color[ 2 ];
- src.data[ off + 3 ] = 255;
- }
- }
- }
- workCtx.putImageData( src, left, top );
- pickCtx.drawImage( workCtx.canvas, 0, 0 );
- // handler(outlineCtx, geometry, stroke);
- }
- const source = await shapefile.open( 'TM_WORLD_BORDERS-0.3.shp' );
- const areas = [];
- for ( let i = 0; ; ++ i ) {
- const { done, value } = await source.read();
- if ( done ) {
- break;
- }
- areas.push( value );
- draw( value );
- if ( i % 20 === 19 ) {
- await wait();
- }
- }
- console.log( JSON.stringify( areas ) );
- console.log( 'min', min );
- console.log( 'max', max );
- console.log( JSON.stringify( countriesById, null, 2 ) );
- const pick = pickCtx.getImageData( 0, 0, pickCtx.canvas.width, pickCtx.canvas.height );
- const outline = outlineCtx.getImageData( 0, 0, outlineCtx.canvas.width, outlineCtx.canvas.height );
- function getId( imageData, x, y ) {
- const off = ( ( ( y + imageData.height ) % imageData.height ) * imageData.width + ( ( x + imageData.width ) % imageData.width ) ) * 4;
- return imageData.data[ off + 0 ] +
- imageData.data[ off + 1 ] * 256 +
- imageData.data[ off + 2 ] * 256 * 256 +
- imageData.data[ off + 3 ] * 256 * 256 * 256;
- }
- function putPixel( imageData, x, y, color ) {
- const off = ( y * imageData.width + x ) * 4;
- imageData.data.set( color, off );
- }
- for ( let y = 0; y < pick.height; ++ y ) {
- for ( let x = 0; x < pick.width; ++ x ) {
- const s = getId( pick, x, y );
- const r = getId( pick, x + 1, y );
- const d = getId( pick, x, y + 1 );
- let v = 0;
- if ( s !== r || s !== d ) {
- v = 255;
- }
- putPixel( outline, x, y, [ v, v, v, v ] );
- }
- }
- for ( let y = 0; y < outline.height; ++ y ) {
- for ( let x = 0; x < outline.width; ++ x ) {
- const s = getId( outline, x, y );
- const l = getId( outline, x - 1, y );
- const u = getId( outline, x, y - 1 );
- const r = getId( outline, x + 1, y );
- const d = getId( outline, x, y + 1 );
- //const rd = getId(outline, x + 1, y + 1);
- let v = s;
- if ( ( s && r && d ) ||
- ( s && l && d ) ||
- ( s && r && u ) ||
- ( s && l && u ) ) {
- v = 0;
- }
- putPixel( outline, x, y, [ v, v, v, v ] );
- }
- }
- outlineCtx.putImageData( outline, 0, 0 );
- }
- function wait( ms = 0 ) {
- return new Promise( ( resolve ) => {
- setTimeout( resolve, ms );
- } );
- }
- main();
|