diff --git a/mayfinal.js b/mayfinal.js new file mode 100644 index 0000000..000af72 --- /dev/null +++ b/mayfinal.js @@ -0,0 +1,310 @@ +let map = document.getElementById('map'); +let coord = document.getElementById('coord'); +map.width = map.clientWidth; +map.height = map.clientHeight; +let ctx = map.getContext('2d'); + +const tilesize = 256; +const minlevel = 3; +const maxlevel = 18; +let zoom_level = 3; //默认 + +let viewport = {}; +viewport.width = map.width; //默认已知 +viewport.height = map.height; //默认已知 +viewport.centerx = Math.pow(2, zoom_level) * 256 / 2; //默认 +viewport.centery = Math.pow(2, zoom_level) * 256 / 2; //默认 + +viewport.xoffset = (viewport.width / 2 - viewport.centerx); +viewport.yoffset = (viewport.height / 2 - viewport.centery); + +let needtiles = new Array(); +let cache = new Array(); + +let alltilecount = 0; +let downtilecount = 0; +let drecord = new Array(); + +function calTileSize() { + let etsize = tilesize; + if (zoom_level != Math.floor(zoom_level)) { + etsize = tilesize * Math.pow(2, zoom_level - Math.floor(zoom_level + 1)); + } + return etsize; +} + +function calTileNum(coord) { + return Math.floor(coord / calTileSize()); +} + +function tilesfilter(tails) { + let news = new Array(); + let max = Math.pow(2, zoom_level); + if (zoom_level != Math.floor(zoom_level)) { + max = Math.pow(2, Math.floor(zoom_level + 1)); + } + for (let i = 0; i < tails.length; i++) { + const e = tails[i]; + if (e.x >= 0 && e.y >= 0 && e.x < max && e.y < max) { + news.push(e); + } + } + return news; +} + + +function clearcanvas() { + ctx.clearRect(0, 0, map.width, map.height); +} + +function drawRect(x, y, width, height) { + ctx.strokeStyle = "white" + ctx.strokeRect(x, y, width, height); +} + +function drawtext(text, x, y) { + ctx.fillStyle = "white" + ctx.font = "18px serif"; + ctx.textAlign = "center"; + ctx.textBaseline = "middle"; + ctx.fillText(text, x, y); +} + +function mplat2viewport(mx, my) { + const vx = mx + viewport.xoffset; + const vy = my + viewport.yoffset; + return { x: vx, y: vy }; +} + +function viewport2mplat(vx, vy) { + const mx = vx - viewport.xoffset; + const my = vy - viewport.yoffset; + return { x: mx, y: my }; +} + +function updateviewporttest(scrollx, scrolly, oldz, newz) { + //计算出缩放后坐标系的偏移值 + // xv = xm + offset ; offset = xv - xm + //const newz = zoom_level; + const scrollm = viewport2mplat(scrollx, scrolly); + //const basescale = 2 * Math.pow(2, newz) / Math.pow(2, Math.floor(newz) + 1); + console.log('zoomlevel : ', newz, oldz); + const basescale = Math.pow(2, newz - oldz); + console.log('basescale: ', basescale); + const newscrollm = { x: scrollm.x * basescale, y: scrollm.y * basescale }; + console.log('缩放点新坐标 ', newscrollm); + viewport.xoffset = scrollx - newscrollm.x; + viewport.yoffset = scrolly - newscrollm.y; +} + +//test +function drawtest() { + ctx.clearRect(0, 0, map.width, map.height); + const viewport_left_top = viewport2mplat(0, 0); + const viewport_right_bottom = viewport2mplat(viewport.width, viewport.height); + const mixnum = calTileNum(viewport_left_top.x); + const miynum = calTileNum(viewport_left_top.y); + const maxnum = calTileNum(viewport_right_bottom.x); + const maynum = calTileNum(viewport_right_bottom.y); + const horicount = maxnum - mixnum + 1; + const verticount = maynum - miynum + 1; + + clearcanvas(); + let tails = new Array(); + for (let i = 0; i < horicount; i++) { + for (let j = 0; j < verticount; j++) { + const x = mixnum + i; + const y = miynum + j; + tails.push({ x: x, y: y }); + } + } + needtiles = tilesfilter(tails); + alltilecount += needtiles.length; + //console.log(needtiles.length, needtiles); + needtiles.forEach(et => { + const etsize = calTileSize(); + //const tilev = mplat2viewport(et.x * etsize, et.y * etsize); + //drawRect(tilev.x, tilev.y, etsize, etsize); + let z = zoom_level; + if (zoom_level != Math.floor(zoom_level)) { + z = Math.floor(zoom_level + 1); + } + const text = `${z}/${et.x}/${et.y}.png`; + console.log(text); + //drawtext(text, tilev.x + etsize / 2, tilev.y + etsize / 2); + drawTileImg(z, et.x, et.y); + }); + debug(); +} + +let moveflag = false; +let downpoing = { x: 0, y: 0 }; +map.onmousedown = (e) => { + console.log('鼠标按下'); + moveflag = true; + downpoing.x = e.offsetX; + downpoing.y = e.offsetY; + console.log(coord.innerText); +} +map.onmouseup = (e) => { + console.log('鼠标松开'); + moveflag = false; +} +map.onmousemove = (e) => { + //输出latlng + const mlc = viewport2mplat(e.offsetX, e.offsetY); + let lat = tile2lat(mlc.y / tilesize, zoom_level); + let lng = tile2long(mlc.x / tilesize, zoom_level); + // const etsize = calTileSize(); + // if (zoom_level != Math.floor(zoom_level)) { + // console.log('resize', etsize, tilesize, lat, lng) + // lat = tile2lat(mlc.y / etsize * tilesize, Math.floor(zoom_level + 1)); + // lng = tile2long(mlc.x / etsize * tilesize, Math.floor(zoom_level + 1)); + // } + + const coordstr = `(${e.offsetX},${e.offsetY}) - (${mlc.x.toFixed(3)},${mlc.y.toFixed(3)}) - (${lat},${lng})`; + coord.innerText = coordstr; + //console.log(e.offsetX, e.offsetY); + if (moveflag) { + console.log('有效位移'); + const movex = downpoing.x - e.offsetX; + const movey = downpoing.y - e.offsetY; + //实现移动逻辑 + viewport.xoffset -= movex; + viewport.yoffset -= movey; + + drawtest(); + downpoing.x = e.offsetX; + downpoing.y = e.offsetY; + } +} + +map.onwheel = (e) => { + //console.log(e); + const ds = 0.25; + const zoomleveloffset = (e.deltaY > 0) ? 1 * ds : -1 * ds; + const oldz = zoom_level; + zoom_level = zoom_level - zoomleveloffset; + if (zoom_level < minlevel) { + zoom_level = minlevel; + return; + } + + if (zoom_level > maxlevel) { + zoom_level = maxlevel; + return; + } + console.log(zoom_level, e.offsetX, e.offsetY); + updateviewporttest(e.offsetX, e.offsetY, oldz, zoom_level); + drawtest(); +} + +drawtest(); + + + +Array.prototype.remove = function(val) { + const index = this.indexOf(val); + if (index > -1) { + return this.splice(index, 1); + } + return this; +} + +//add opeenstreetmap +function downloadimg(z, x, y) { + return new Promise((resolve, reject) => { + //const url = `https://tile.openstreetmap.org/${z}/${x}/${y}.png`; + const url = 'http://webrd01.is.autonavi.com/' + + 'appmaptile?lang=zh_cn&size=1&scale=1&style=8' + + `&x=${x}&y=${y}&z=${z}`; + //add downloadcheck + const obj = { z: z, x: x, y: y }; + drecord.push(obj); //下载前加入记录 + let img = new Image(); + img.onload = () => { + console.log('downloaded' + z + '/' + x + '/' + y + '.png'); + cache.push({ img: img, z: z, x: x, y: y }); + drecord.remove(obj); //下载完成后从记录中移除 + downtilecount += 1; + resolve(img); + } + img.src = url; + }); +} + +async function getImg(z, x, y) { + //先判断是否加入下载记录 + const isdownloading = drecord.includes({ z: z, x: x, y: y }); + if (isdownloading) { + //return getImg(z, x, y); + return setTimeout(() => { + getImg(z, x, y); + }, 100); + } + const found = cache.find(e => (e.z == z && e.x == x && e.y == y)); + if (found == undefined) { + let p = await downloadimg(z, x, y); + return p; + } else { + console.log('exist' + z + '/' + x + '/' + y + '.png'); + return found.img; + } +} + +function drawTileImg(z, x, y) { + const img = getImg(z, x, y); + img.then((e) => { + const etsize = calTileSize(); + const tilev = mplat2viewport(x * etsize, y * etsize); + let fz = zoom_level; + if (zoom_level != Math.floor(zoom_level)) { + fz = Math.floor(zoom_level + 1); + } + if (needtiles.find(t => z == fz && t.x == x && t.y == y)) { + ctx.drawImage(e, tilev.x, tilev.y, etsize, etsize); + } + }); +} + +function debug() { + console.log('=============debug================='); + console.log('zoom_level', zoom_level); + console.log('alltilecount', alltilecount); + console.log('downtilecount', downtilecount); + console.log('cachecount', caches.length); + console.log('drecord count', drecord.length); + console.log('=============debug================='); +} + +//=====coord about===== +function lon2tile(lon, z) { + return (Math.floor((lon + 180) / 360 * Math.pow(2, z))); +} + +function lat2tile(lat, z) { + return (Math.floor((1 - Math.log(Math.tan(lat * Math.PI / 180) + + 1 / Math.cos(lat * Math.PI / 180)) / Math.PI) / + 2 * Math.pow(2, z))); +} + +function lon2x(lon, z) { + return ((lon + 180) / 360 * Math.pow(2, z)); +} + +function lat2y(lat, z) { + return ((1 - Math.log(Math.tan(lat * Math.PI / 180) + + 1 / Math.cos(lat * Math.PI / 180)) / Math.PI) / + 2 * Math.pow(2, z)); +} + +function tile2long(x, z) { + return (x / Math.pow(2, z) * 360 - 180); +} + +function tile2lat(y, z) { + var n = Math.PI - 2 * Math.PI * y / Math.pow(2, z); + return (180 / Math.PI * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n)))); +} + +//========= \ No newline at end of file