// ==UserScript==
// @name 농지공간포털 지도 다운로더
// @version 0.3
// @description 농지공간포털의 지도를 다운로드 하는 스크립트입니다. 빨간색 버튼을 클릭하면 다운로드가 시작됩니다.
// @author refracta
// @match https://njy.mafra.go.kr/map/mapMain.do
// @icon https://www.google.com/s2/favicons?sz=64&domain=go.kr
// @grant none
// @license MIT
// @namespace https://greasyfork.org/users/467840
// ==/UserScript==
(async function () {
function waitFor(checkFunction, checkDelay = 100) {
return new Promise(resolve => {
let i = setInterval(_ => {
try {
let check = checkFunction();
check ? clearInterval(i) || resolve(check) : void 0
} catch (e) {
}
}, checkDelay);
});
}
let map = await waitFor(_ => git.map);
function sleep(ms) {
return new Promise(r => setTimeout(r, ms));
}
function hashImageData(imageData) {
return imageData.data.reduce((hash, byte) => hash + byte, 0);
}
function waitCanvas(samplingTime = 250, n = 4) {
let canvas = document.querySelector('canvas');
return new Promise(async (resolve, reject) => {
const context = canvas.getContext('2d');
let queue = [hashImageData(context.getImageData(0, 0, canvas.width, canvas.height))];
let currentHash;
do {
await sleep(samplingTime);
currentHash = hashImageData(context.getImageData(0, 0, canvas.width, canvas.height));
queue.push(currentHash);
queue = queue.slice(-n);
} while (!(queue.length === n && queue.reduce((a, c) => a && currentHash === c, true)))
resolve();
});
}
function getOutline() {
let canvas = document.querySelector('canvas');
let ctx = canvas.getContext('2d');
let image = ctx.getImageData(0, 0, canvas.width, canvas.height);
let pixels = image.data;
let width = image.width;
let height = image.height;
let up = [];
let down = [];
let left = [];
let right = [];
for (let x = 0; x < width; x++) {
up.push(pixels.slice(x * 4, x * 4 + 4));
down.push(pixels.slice((width * (height - 1) + x) * 4, (width * (height - 1) + x) * 4 + 4));
}
for (let y = 0; y < height; y++) {
left.push(pixels.slice(y * width * 4, y * width * 4 + 4));
right.push(pixels.slice((y * width + (width - 1)) * 4, (y * width + (width - 1)) * 4 + 4));
}
return {up, down, left, right};
}
function setAirMode(visible) {
git.layer.getLayerById('AIR_21', map).setVisible(visible);
}
function arePixelArraysEqual(arr1, arr2, targetRate = 0.0009) {
if (arr1.length !== arr2.length) {
return false;
}
let count = 0;
for (let i = 0; i < arr1.length; i++) {
for (let j = 0; j < 4; j++) {
if (arr1[i][j] !== arr2[i][j]) {
count++;
}
}
}
let missRate = count / (arr1.length * 4);
console.log(`missRate: ${missRate * 100}%`);
return missRate <= targetRate;
}
function saveCanvas(name = 'canvas-image.png') {
console.log(`saveCanvas(${name})`);
let canvas = document.querySelector("canvas");
let dataURL = canvas.toDataURL("image/png");
let link = document.createElement("a");
link.href = dataURL;
link.download = name;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
function createElement(str) {
let div = document.createElement('div');
div.innerHTML = str;
let container = document.createDocumentFragment();
for (let i = 0; i < div.childNodes.length; i++) {
let node = div.childNodes[i].cloneNode(true);
container.appendChild(node);
}
return container.childNodes[0];
}
document.querySelector('.mapControl_list').append(createElement(`<li>
<div id="autoDownload" class="item">
<a href="#" title="다운로드" class="btn btn_map_control15" style="background-color: red">
<span>다운로드</span>
</a>
</div>
</li>`));
function updateText(text) {
let pinpoint = document.querySelector('.location_wrap > .pinpoint');
let notificationBar = document.querySelector('#notification-bar');
if (!notificationBar) {
let span = document.createElement('span');
span.id = 'notification-bar';
pinpoint.append(span);
notificationBar = span;
}
notificationBar.textContent = ' ※ ' + text;
}
async function run(saveData) {
await waitCanvas();
let view = map.getView();
let size = map.getSize();
size = size.map(Math.floor);
let resolution = view.getResolution();
let screenMeters = size.map(s => s * resolution);
let startCenter = window.startCenter || view.getCenter();
let zoomLevel = window.zoomLevel || view.getZoom();
view.setZoom(zoomLevel);
view.setCenter(startCenter);
await waitCanvas();
let range2D = window.range2D || [1000, 200];
range2D[1] = -range2D[1];
let airMode = window.airMode !== undefined ? window.airMode : true;
let endCenter = startCenter.map((e, i) => e + range2D[i]);
async function move(x, y) {
view.setCenter([x, y]);
await waitCanvas();
}
async function moveRelative(xDelta, yDelta) {
let center = view.getCenter();
await move(center[0] + xDelta, center[1] + yDelta);
}
const deltaMap = {};
async function movePrecisely(direction, previousOutline) {
console.log(`movePrecisely(${direction})`);
let originalCenter = view.getCenter();
if (direction === 'up') {
await moveRelative(0, screenMeters[1] + resolution);
} else if (direction === 'down') {
await moveRelative(0, -screenMeters[1] - resolution);
} else if (direction === 'left') {
await moveRelative(-screenMeters[0] - resolution, 0);
} else if (direction === 'right') {
await moveRelative(screenMeters[0] + resolution, 0);
}
let center = view.getCenter();
let counter = 1;
let deltas = Object.keys(deltaMap).sort((a, b) => deltaMap[b] - deltaMap[a]);
while (true) {
let delta;
if (deltas.length > 0) {
delta = deltas.shift();
} else {
delta = counter;
while (deltaMap[delta]) {
delta++;
}
counter = delta + 1;
}
if (delta > 10) {
localStorage.forceRun = true;
location.reload();
}
console.log(`center=${view.getCenter()} direction=${direction}, delta=${delta}, deltaMap=${JSON.stringify(deltaMap)}`);
if (direction === 'up') {
await move(center[0], center[1] - resolution * delta);
} else if (direction === 'down') {
await move(center[0], center[1] + resolution * delta);
} else if (direction === 'left') {
await move(center[0] + resolution * delta, center[1]);
} else if (direction === 'right') {
await move(center[0] - resolution * delta, center[1]);
}
let pixelCheck;
if (direction === 'up') {
pixelCheck = arePixelArraysEqual(previousOutline.up, getOutline().down);
} else if (direction === 'down') {
pixelCheck = arePixelArraysEqual(previousOutline.down, getOutline().up);
} else if (direction === 'left') {
pixelCheck = arePixelArraysEqual(previousOutline.left, getOutline().right);
} else if (direction === 'right') {
pixelCheck = arePixelArraysEqual(previousOutline.right, getOutline().left);
}
if (pixelCheck) {
if (direction === 'up') {
await moveRelative(0, resolution);
} else if (direction === 'down') {
await moveRelative(0, -resolution);
} else if (direction === 'left') {
await moveRelative(-resolution, 0);
} else if (direction === 'right') {
await moveRelative(resolution, 0);
}
deltaMap[delta] = deltaMap[delta] === undefined ? 1 : deltaMap[delta] + 1;
return;
}
}
}
if (Object.keys(saveData).length !== 0) {
console.log('Continue from saveData');
startCenter = saveData.startCenter;
range2D = saveData.range2D;
airMode = saveData.airMode;
endCenter = saveData.endCenter;
zoomLevel = saveData.zoomLevel;
}
console.log(`size: ${size}`);
console.log(`resolution: ${resolution}`);
console.log(`screenMeters: ${screenMeters}`);
console.log(`startCenter: ${startCenter}`);
console.log(`endCenter: ${endCenter}`);
let outline = getOutline();
let newCenter = saveData.newCenter || startCenter;
for (let y = saveData.y || startCenter[1], yCount = saveData.yCount || 0;
y > endCenter[1];
y = newCenter[1], yCount++) {
let yCenter = saveData.yCenter || newCenter;
for (let x = saveData.x || startCenter[0], xCount = saveData.xCount || 0;
x < endCenter[0];
x = newCenter[0], xCount++) {
if (Object.keys(saveData).length !== 0) {
saveData = {};
}
updateText(`CurrentProcess: ${Math.floor(x - startCenter[0])}/${Math.floor(endCenter[0] - startCenter[0])}, ${Math.floor(startCenter[1] - y)}/${Math.floor(startCenter[1] - endCenter[1])}`)
console.log(`lastCenter: ${view.getCenter()}`);
let currentCenter = view.getCenter();
if (!(x === currentCenter[0] && y === currentCenter[1])) {
view.setZoom(zoomLevel);
view.setCenter([x, y]);
await waitCanvas();
outline = getOutline();
}
// Core logic
if (airMode) {
setAirMode(true);
await waitCanvas();
}
saveCanvas(`${xCount}x${yCount}.png`);
if (airMode) {
setAirMode(false);
await waitCanvas();
}
if (window.currentSaveData) {
localStorage.isRunning = true;
localStorage.saveData = JSON.stringify(window.currentSaveData);
}
window.currentSaveData = {
x: x,
y: y,
xCount: xCount,
yCount: yCount,
newCenter,
startCenter,
range2D,
airMode,
endCenter,
yCenter,
zoomLevel: view.getZoom()
};
// Next X
await movePrecisely('right', outline);
newCenter = view.getCenter();
outline = getOutline();
}
// Next Y
await move(yCenter[0], yCenter[1]);
await movePrecisely('down', getOutline());
newCenter = view.getCenter();
outline = getOutline();
}
localStorage.isRunning = false;
localStorage.forceRun = false;
}
let c1, c2;
map.on('singleclick', function (e) {
c1 = e.coordinate;
console.log(`startCenter: ${c1}, endCenter: ${c2}`);
if (c1 && c2) {
console.log(`range2D: ${c1.map((e, i) => Math.abs(e - c2[i]))}`);
}
});
map.getViewport().addEventListener('contextmenu', function (e) {
e.preventDefault();
c2 = map.getEventCoordinate(e);
console.log(`startCenter: ${c1}, endCenter: ${c2}`);
if (c1 && c2) {
console.log(`range2D: ${c1.map((e, i) => Math.abs(e - c2[i]))}`);
}
});
if (localStorage.isRunning === 'true') {
if (localStorage.forceRun === 'true' || confirm('Continue?')) {
localStorage.forceRun = false;
await run(JSON.parse(localStorage.saveData));
} else {
localStorage.isRunning = false;
localStorage.forceRun = false;
}
}
document.querySelector('#autoDownload').addEventListener('click', _ => run({}));
// window.zoomLevel = 11;
// window.startCenter = [185386.5028661047, 395956.31206765346];
// window.range2D = [28285.49491734401, 31374.144608640752];
})();