123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241 |
- '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();
|