diff --git a/README.md b/README.md index bc2fb14..3814a00 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,13 @@ -对接他人提供的底图服务 - -然后是开发自己的底图服务,结构可以参考现成已有的 - - -## 参考链接 -https://xcsf.github.io/blog/2020/06/12/%E7%93%A6%E7%89%87Tile%E5%9C%B0%E5%9B%BE%E5%8E%9F%E7%90%86/ -https://xcsf.github.io/blog/2020/06/12/%E7%93%A6%E7%89%87Tile%E8%A1%8C%E5%88%97%E5%8F%B7%E8%AE%A1%E7%AE%97%E6%96%B9%E6%B3%95/ -https://github.com/RLwu/GIS -https://switch2osm.org/using-tiles/getting-started-with-leaflet/ -https://leafletjs.cn/ -https://zh.wikipedia.org/wiki/%E9%BA%A5%E5%8D%A1%E6%89%98%E6%8A%95%E5%BD%B1%E6%B3%95 -https://zh.wikipedia.org/wiki/Web%E5%A2%A8%E5%8D%A1%E6%89%98%E6%8A%95%E5%BD%B1 -https://web.archive.org/web/20220421120137/https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames -https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames -https://en.wikipedia.org/wiki/Web_Mercator_projection -https://web.archive.org/web/20141009142830/http://earth-info.nga.mil/GandG/wgs84/web_mercator/(U)%20NGA_SIG_0011_1.0.0_WEBMERC.pdf -https://www.openstreetmap.org/#map=15/22.7602/114.3888&layers=D -https://en.wikipedia.org/wiki/Eccentricity_(mathematics)#:~:text=The%20eccentricity%20of%20an%20ellipse%20is%2C%20most%20simply%2C%20the%20ratio,of%20the%20semimajor%20axis%20a.&text=(Flattening%20may%20be%20denoted%20by,if%20f%20is%20linear%20eccentricity.) - -http://wiki.gis.com/wiki/index.php/Geodetic_system -https://developer.mozilla.org/zh-CN/docs/Web/API/CanvasRenderingContext2D/textBaseline -https://developer.mozilla.org/zh-CN/docs/Web/API/CanvasRenderingContext2D/textAlign \ No newline at end of file +## 项目概述 +为地图类网页app添加底图支持,目前仅支持加载web墨卡托的瓦片底图 +暂时选择的是gaode的普通底图,可无缝切换为openstreetmap底图服务 + +## Features +将屏幕坐标系映射到投影平面,为坐标转化提供支持,见viewport +将viewport和底图加载的逻辑分离开 +事件和绘制逻辑在basemap中,对外直接使用basemap挂载到canvas就行 (参考index.js) +支持设置初始位置,和记忆上次查看位置 + +## tips +坐标转化 是直接进行像素级的映射 +缩放在瓦片地图那一层已经定义好了,不需要再额外转化 \ No newline at end of file diff --git a/add web b/add web deleted file mode 100644 index 89c71db..0000000 --- a/add web +++ /dev/null @@ -1,32 +0,0 @@ - web: - image: nginx - ports: - - 1080:80 - volumes: - - ./nginx_conf/nginx:/etc/nginx - - ./dist:/usr/share/nginx/html:ro - -#include /etc/nginx/conf.d/*.conf; - server { - listen 80; - #listen [::]:80; - server_name localhost; - - location / { - root /usr/share/nginx/html; - index index.html index.htm; - } - - location /m { - rewrite .* /index.html permanent; - } - - #error_page 404 /404.html; - # redirect server error pages to the static page /50x.html - # - error_page 500 502 503 504 /50x.html; - location = /50x.html { - root /usr/share/nginx/html; - } - } - diff --git a/ellipsode.js b/ellipsode.js deleted file mode 100644 index 5cbca6e..0000000 --- a/ellipsode.js +++ /dev/null @@ -1,305 +0,0 @@ -//椭球相关参数计算 -const a = 6378137; //长半轴 a -const f = 1 / 298.257223563; //扁率 (a-b)/a -const b = a * (1 - f);//短半轴 b -const e = Math.sqrt(a * a - b * b) / a;//椭球第一偏心率 - -// console.log(a); -// console.log(f); -// console.log(b); -// console.log(e); -// console.log(e*e); - -function degToRad(deg) { - return deg / 180 * Math.PI; -} - -function radToDeg(rad) { - return rad / Math.PI * 180; -} - -function wgs84ToWebMercator(lat, lon) { - let x = a * lon; - let y = a * Math.log(Math.tan(Math.PI / 4 + lat / 2)); - return { x: x, y: y }; -} - -function webMercatorToWgs84(x, y) { - let lon = x / a; - let lat = 2 * Math.atan(Math.pow(Math.E, y / a)) - Math.PI / 2; - return { lon: lon, lat: lat } -} - -// console.log(wgs84ToWebMercator(degToRad(50), degToRad(50))); -// console.log(wgs84ToWebMercator(degToRad(49.99999999999999), degToRad(49.99999999999999))); -// let wgs = webMercatorToWgs84(5565974.539663678, 6446275.841017158); -// console.log(wgs); -// console.log('lat: ' + radToDeg(wgs.lat) + ',' -// + 'lon:' + radToDeg(wgs.lon)); - - -// console.log(2 * Math.atan(Math.pow(Math.E, 0 / a)) - Math.PI / 2); -// console.log(radToDeg(webMercatorToWgs84(5565974.539663678, 0).lat)); - -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)))); -} - -//test -//22.7490073, 114.3781213 z=15 -//let mappiece = Math.pow(2, zoom_level); //一个方向的地图块数 1块256个像素 - -// console.log(lon2tile(long, zoom_level)); -// console.log(lat2tile(lat, zoom_level)); -// console.log(lon2x(long, zoom_level)); -// console.log(lat2y(lat, zoom_level)); - -// console.log('----------------------'); -// console.log(tile2long(26794, zoom_level)); -// console.log(tile2long(26794 + 1 / 256, zoom_level)); - -//https://tile.openstreetmap.org/z/x/y.png -//https://tile.openstreetmap.org/15/26794/14256.png - -//已知条件 lat long zoom_level 显示范围的宽高 -let zoom_level = 15; -let lat = 22.7490073; -let long = 114.3781213; -let width = 1600; -let height = 900; - -function calBasetile(lat, long, zoom_level) { - let basetile = {}; - basetile.x = lon2x(long, zoom_level);//对应zoomlevel下long的x坐标 - basetile.y = lat2y(lat, zoom_level);//对应zoomlevel下lat的y坐标 - basetile.tilex = lon2tile(long, zoom_level);//对应zoomlevel下long的x tile编号 - basetile.tiley = lat2tile(lat, zoom_level);//对应zoomlevel下lat的y tile编号 - basetile.offsetx = Math.floor((basetile.x - basetile.tilex) * 256);//相对于左上角,其点的x偏移值 - basetile.offsety = Math.floor((basetile.y - basetile.tiley) * 256);//相对于左上角,其点的y偏移值 - - basetile.minx = width / 2 - basetile.offsetx; - basetile.maxx = basetile.minx + 256; - basetile.miny = height / 2 - basetile.offsety; - basetile.maxy = basetile.miny + 256; - - basetile.p1 = Math.floor(basetile.maxy / 256 + 0.5); - basetile.p2 = Math.floor((height - basetile.miny) / 256 + 0.5); - basetile.p3 = Math.floor(basetile.maxx / 256 + 0.5); - basetile.p4 = Math.floor((width - basetile.minx) / 256 + 0.5); - console.log(basetile); - return basetile; -} - -let basetile = calBasetile(lat, long, zoom_level); - -// let x = lon2x(long, zoom_level); -// let y = lat2y(lat, zoom_level); - -// let tilex = lon2tile(long, zoom_level); -// let tiley = lat2tile(lat, zoom_level); - -// let offsetx = Math.floor((x - tilex) * 256); -// let offsety = Math.floor((y - tiley) * 256); - -// //计算出瓦片地图的基准坐标,假设显示的宽高为1600*900 -// let minx = width / 2 - offsetx; -// let maxx = minx + 256; -// let miny = height / 2 - offsety; -// let maxy = miny + 256; - -//计算出四个方向各需要的块数 -//p1 p2 p3 p4 上 下 左 右 -// let p1 = Math.floor(maxy / 256 + 0.5); -// let p2 = Math.floor((height - miny) / 256 + 0.5); -// let p3 = Math.floor(maxx / 256 + 0.5); -// let p4 = Math.floor((width - minx) / 256 + 0.5); - -//基于已知条件计算需要的各个tile -function needtiles(basetile, p1, p2, p3, p4) { - let tiles = new Array(); - let tilex = basetile.tilex; - let tiley = basetile.tiley; - tiles.push({ x: tilex, y: tiley }); - for (let i = 0; i < p4; i++) { - tiles.push({ x: tilex + i + 1, y: tiley }); - } - for (let i = 0; i < p3; i++) { - tiles.push({ x: tilex - i - 1, y: tiley }); - } - for (let i = 0; i < p1; i++) { - for (let j = 0; j < p3 + p4 + 1; j++) { - tiles.push({ x: tilex - p3 + j, y: tiley - i - 1 }); - } - } - for (let i = 0; i < p2; i++) { - for (let j = 0; j < p3 + p4 + 1; j++) { - tiles.push({ x: tilex - p3 + j, y: tiley + i + 1 }); - } - } - //console.log(tiles); - //console.log(tiles.length); - return tiles; -} - -let tiles = needtiles(basetile, basetile.p1, basetile.p2, basetile.p3, basetile.p4); - - -//渲染问题 -//通过与基准之间的位置关系计算渲染的坐标 -//minx miny 即为基准tile当前渲染位置 - -// for (let i = 0; i < tiles.length; i++) { -// const element = tiles[i]; -// let renderx = (element.x - tilex) * 256 + minx; -// let rendery = (element.y - tiley) * 256 + miny; -// console.log({ x: renderx, y: rendery }); -// } - -let canvas = document.getElementById('map'); -canvas.width = map.clientWidth; -canvas.height = map.clientHeight; -let ctx = canvas.getContext('2d'); - -function clearcanvas() { - ctx.clearRect(0, 0, canvas.width, canvas.height); -} - -function drawPoint(x, y) { - ctx.fillStyle = "white" - ctx.strokeStyle = "#a35312" - ctx.beginPath(); - ctx.arc(x, y, 3, 0, Math.PI * 2, true); - ctx.closePath(); - ctx.fill(); - ctx.stroke(); -} - -function drawRect(x, y) { - ctx.strokeStyle = "white" - ctx.strokeRect(x, y, 256, 256); -} - -function drawtext(text, x, y) { - ctx.fillStyle = "white" - ctx.font = "18px serif"; - ctx.textAlign = "center"; - ctx.textBaseline = "middle"; - ctx.fillText(text, x, y); -} - -function drawgrid(basetile, tiles) { - let minx = basetile.minx; - let miny = basetile.miny; - let offsetx = basetile.offsetx; - let offsety = basetile.offsety; - let tilex = basetile.tilex; - let tiley = basetile.tiley; - clearcanvas(); - drawPoint(minx + offsetx, miny + offsety); - for (let i = 0; i < tiles.length; i++) { - const element = tiles[i]; - let renderx = (element.x - tilex) * 256 + minx; - let rendery = (element.y - tiley) * 256 + miny; - //console.log({ x: renderx, y: rendery }); - drawPoint(renderx, rendery); - drawRect(renderx, rendery); - const showtext = 'x:' + element.x + ' ' + 'y:' + element.y; - //drawtext(showtext, renderx + 256 / 2, rendery + 256 / 2); - drawtext(showtext, renderx + showtext.length / 4 * 18 + 4, rendery + 9); - } -} - -drawgrid(basetile, tiles); - -// drawPoint(width / 2, height / 2); - -// const element = tiles[0]; -// let renderx = (element.x - tilex) * 256 + minx; -// let rendery = (element.y - tiley) * 256 + miny; -// drawPoint(renderx, rendery); -// drawPoint(renderx + 256 / 2, rendery + 256 / 2); -// drawRect(renderx, rendery); - -// drawtext(showtext, renderx + 256 / 2, rendery + 256 / 2); -// console.log({ x: renderx, y: rendery }); - -//实现移动缩放方法 -//缩放 -map.onwheel = (e) => { - //console.log(e); - const zoomleveloffset = (e.deltaY > 0) ? 1 : -1; - zoom_level -= zoomleveloffset; - if (zoom_level < 0) { - zoom_level = 0; - return; - } - - if (zoom_level > 19) { - zoom_level = 19; - return; - } - console.log(zoom_level); - //clearcanvas(); - //重新计算basetile - basetile = calBasetile(lat, long, zoom_level); - tiles = needtiles(basetile, basetile.p1, basetile.p2, basetile.p3, basetile.p4); - drawgrid(basetile, tiles); -} - -let moveflag = false; -let downpoing = { x: 0, y: 0 }; - -map.onmousedown = (e) => { - console.log('鼠标按下'); - moveflag = true; - downpoing.x = e.offsetX; - downpoing.y = e.offsetY; -} - -map.onmouseup = (e) => { - console.log('鼠标松开'); - moveflag = false; -} - -map.onmousemove = (e) => { - if (moveflag) { - console.log('有效位移'); - const movex = downpoing.x - e.offsetX; - const movey = downpoing.y - e.offsetY; - //实现移动逻辑 - let xp = basetile.x + movex / 256;//注意像素移动要除以256 - let yp = basetile.y + movey / 256; - long = tile2long(xp, zoom_level); - lat = tile2lat(yp, zoom_level); - basetile = calBasetile(lat, long, zoom_level); - tiles = needtiles(basetile, basetile.p1, basetile.p2, basetile.p3, basetile.p4); - drawgrid(basetile, tiles); - - downpoing.x = e.offsetX; - downpoing.y = e.offsetY; - } -} \ No newline at end of file diff --git a/index.html b/index.html index 097895d..5d801cc 100644 --- a/index.html +++ b/index.html @@ -1,43 +1,51 @@ - - - - - tilerendertest - - - -
-
- -
+ + + + + + tilerendertest + + + + +
+
+
- - +
+ + + \ No newline at end of file diff --git a/index.js b/index.js new file mode 100644 index 0000000..8e4255c --- /dev/null +++ b/index.js @@ -0,0 +1,10 @@ +import * as Basemap from './src/basemap.js' +document.addEventListener('contextmenu', function (event) { + event.preventDefault() // 阻止浏览器默认右键菜单的显示 +}) + +Basemap.setDomById('map') +Basemap.setCanvasSize() +window.addEventListener('resize', Basemap.setCanvasSize) + +// Basemap.setCenterByWgs84(22.54, 114.06, 18) diff --git a/mayfinal.js b/mayfinal.js deleted file mode 100644 index f700297..0000000 --- a/mayfinal.js +++ /dev/null @@ -1,333 +0,0 @@ -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 viewport2wgs84(vx, vy) { - const mlc = viewport2mplat(vx, vy); - //数据超界处理, 这里对应缩放等级的地图的大小是 - const limit = calTileSize() * Math.pow(2, zoom_level); - console.log(calTileSize()); - if (vx <= 0 || vy <= 0 || vx >= limit || vy >= limit) { - throw "OutMapRange"; - } - const lat = tile2lat(mlc.y / tilesize, zoom_level); - const lng = tile2long(mlc.x / tilesize, zoom_level); - return { lat: lat, lng: lng } -} - -function wgs84toviewport(lat, lng) { - //数据超界处理 经度 -180 180 - //85.0511287798066 (2*Math.atan(Math.pow(Math.E,Math.PI))-Math.PI/2)/Math.PI*180 - const latlimit = 85.051128; - const lngp = lng - Math.floor((lng + 180) / 360) * 360; - if (lat < (0 - latlimit) || lat > latlimit) { - throw "OutMapRange"; - } - const my = lat2y(lat, zoom_level) * tilesize; - const mx = lon2x(lngp, zoom_level) * tilesize; - const vp = mplat2viewport(mx, my); - return { x: vp.x, y: vp.y }; -} - - -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(zoom_level, e.offsetX, e.offsetY); - const wgs84 = viewport2wgs84(e.offsetX, e.offsetY); - console.log(wgs84, 'check', wgs84toviewport(wgs84.lat, wgs84.lng)); -} -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 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 diff --git a/package.json b/package.json new file mode 100644 index 0000000..8335f9e --- /dev/null +++ b/package.json @@ -0,0 +1,17 @@ +{ + "name": "formaptile", + "version": "1.0.0", + "description": "", + "main": "index.js", + "type": "module", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "http://atsw.top:3000/alimu/formaptile.git" + }, + "keywords": [], + "author": "", + "license": "ISC" +} diff --git a/src/basemap.js b/src/basemap.js new file mode 100644 index 0000000..d9a9d44 --- /dev/null +++ b/src/basemap.js @@ -0,0 +1,220 @@ +import { + viewport, + calTileNum, + calTileSize, + setViewportSize, + setViewportCenterByWgs84, + setViewportCenterByMap, + map2viewport, + viewport2map, + updateZoomLevel, +} from './viewport.js' + +import * as Tiles from './tiles.js' + +let map = undefined + +export function setDomById(id) { + map = document.getElementById(id) + console.debug('底图挂载在', map) + //添加响应事件 + // let moveflag = false + // let downpoing = { x: 0, y: 0 } + const minlevel = 3 + const maxlevel = 18 + map.onwheel = (e) => { + const ds = 0.25 + const zoomleveloffset = e.deltaY > 0 ? 1 * ds : -1 * ds + let newz = viewport.zoom_level + newz = newz - zoomleveloffset + if (newz < minlevel) { + newz = minlevel + return + } + + if (newz > maxlevel) { + newz = maxlevel + return + } + updateZoomLevel(e.offsetX, e.offsetY, newz) + savePosInfo() + drawGrid() + } + + 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(viewport.zoom_level, e.offsetX, e.offsetY) + // try { + // const wgs84 = viewport2wgs84(e.offsetX, e.offsetY) + // console.log( + // 'check', + // { x: e.offsetX, y: e.offsetY }, + // wgs84, + // wgs84toviewport(wgs84.lat, wgs84.lon) + // ) + // } catch (error) { + // //console.log(error) + // } + } + map.onmouseup = (e) => { + //console.log('鼠标松开') + moveflag = false + } + map.onmousemove = (e) => { + //输出latlng + //const mlc = viewport2map(e.offsetX, e.offsetY) + // const wgs84 = viewport2wgs84(e.offsetX, e.offsetY) + // const coordstr = `(${e.offsetX},${e.offsetY})->(${mlc.x.toFixed( + // 3 + // )},${mlc.y.toFixed(3)})->(${wgs84.lat},${wgs84.lon})` + // console.log(coordstr) + if (moveflag) { + //console.log('有效位移') + const movex = downpoing.x - e.offsetX + const movey = downpoing.y - e.offsetY + //实现移动逻辑 + viewport.xoffset -= movex + viewport.yoffset -= movey + savePosInfo() + drawGrid() + downpoing.x = e.offsetX + downpoing.y = e.offsetY + } + } +} +export function getContext() { + if (map) { + return map.getContext('2d') + } +} + +function savePosInfo() { + const mcenter = viewport2map(viewport.width / 2, viewport.height / 2) + let poseInfo = { + mx: mcenter.x, + my: mcenter.y, + zoom_level: viewport.zoom_level, + } + localStorage.setItem('poseInfo-nas22udfh12', JSON.stringify(poseInfo)) +} + +export function setCanvasSize() { + if (map) { + map.width = map.clientWidth + map.height = map.clientHeight + //console.debug('重新设置大小', map.width, map.height) + //init + setViewportSize(map.width, map.height) + //随机用一个标识 + const poseInfoStr = localStorage.getItem('poseInfo-nas22udfh12') + const poseInfo = JSON.parse(poseInfoStr) + if (poseInfo) { + setViewportCenterByMap( + poseInfo.mx, + poseInfo.my, + poseInfo.zoom_level + ) + } else { + setViewportCenterByWgs84(0, 0, 3) + } + //draw + drawGrid() + } +} + +export function setCenterByWgs84(lat, lon, z) { + setViewportCenterByWgs84(lat, lon, z) + drawGrid() +} + +function drawRect(x, y, width, height) { + const ctx = getContext() + ctx.strokeStyle = 'white' + ctx.strokeRect(x, y, width, height) +} + +function clear() { + const ctx = getContext() + ctx.clearRect(0, 0, map.width, map.height) +} + +function drawtext(text, x, y) { + const ctx = getContext() + ctx.fillStyle = 'white' + ctx.font = '18px serif' + ctx.textAlign = 'center' + ctx.textBaseline = 'middle' + ctx.fillText(text, x, y) +} + +function drawTileImg(z, x, y) { + const img = Tiles.getImg(z, x, y) + img.then((e) => { + const etsize = calTileSize() + const tilev = map2viewport(x * etsize, y * etsize) + let fz = viewport.zoom_level + if (fz != Math.floor(fz)) { + fz = Math.floor(fz + 1) + } + if (needtiles.find((t) => z == fz && t.x == x && t.y == y)) { + const ctx = getContext() + ctx.drawImage(e, tilev.x, tilev.y, etsize, etsize) + } + }) +} + +function tilesfilter(tails) { + const zoom_level = viewport.zoom_level + 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 +} + +let needtiles = new Array() +export function drawGrid() { + clear() + const viewport_left_top = viewport2map(0, 0) + const viewport_right_bottom = viewport2map(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 + 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) + needtiles.forEach((et) => { + let z = viewport.zoom_level + if (z != Math.floor(z)) { + z = Math.floor(z + 1) + } + // const etsize = calTileSize() + // const tilev = map2viewport(et.x * etsize, et.y * etsize) + //drawRect(tilev.x, tilev.y, etsize, etsize) + //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) + }) +} diff --git a/src/tiles.js b/src/tiles.js new file mode 100644 index 0000000..2cce68c --- /dev/null +++ b/src/tiles.js @@ -0,0 +1,52 @@ +Array.prototype.remove = function (val) { + const index = this.indexOf(val) + if (index > -1) { + return this.splice(index, 1) + } + return this +} + +const cache = new Array() +const downloadingRecord = new Array() + +//const tileserverUrl = +//openstreetmap +//const url = `https://tile.openstreetmap.org/${z}/${x}/${y}.png`; + +function downloadimg(z, x, y) { + return new Promise((resolve, reject) => { + const url = + 'https://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 } + downloadingRecord.push(obj) //下载前加入记录 + let img = new Image() + img.onload = () => { + cache.push({ img: img, z: z, x: x, y: y }) + downloadingRecord.remove(obj) //下载完成后从记录中移除 + resolve(img) + } + img.referrerPolicy = 'no-referrer' + img.src = url + }) +} + +export async function getImg(z, x, y) { + //先判断是否加入下载记录 + const isdownloading = downloadingRecord.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 { + return found.img + } +} diff --git a/src/utils.js b/src/utils.js new file mode 100644 index 0000000..63e8535 --- /dev/null +++ b/src/utils.js @@ -0,0 +1,45 @@ +//=====coord about===== +export function lon2tile(lon, z) { + return Math.floor(((lon + 180) / 360) * Math.pow(2, z)) +} + +export 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) + ) +} + +export function lon2x(lon, z) { + return ((lon + 180) / 360) * Math.pow(2, z) +} + +export 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) + ) +} + +export function tile2long(x, z) { + return (x / Math.pow(2, z)) * 360 - 180 +} + +export 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))) +} + +//========= diff --git a/src/viewport.js b/src/viewport.js new file mode 100644 index 0000000..918fb49 --- /dev/null +++ b/src/viewport.js @@ -0,0 +1,109 @@ +import * as Utils from './utils.js' + +export const viewport = { + width: 256, + height: 256, + zoom_level: 0, + xoffset: 0, + yoffset: 0, +} + +//计算所有情况下的底图大小,包括非整数z +export function calTileSize() { + let etsize = 256 + const zoom_level = viewport.zoom_level + if (zoom_level != Math.floor(zoom_level)) { + etsize = 256 * Math.pow(2, zoom_level - Math.floor(zoom_level + 1)) + } + return etsize +} + +export function calTileNum(coord) { + return Math.floor(coord / calTileSize()) +} + +export function setViewportSize(width, height) { + viewport.width = width + viewport.height = height +} + +export function updateOffset() { + const z = viewport.zoom_level +} + +export function map2viewport(mx, my) { + const vx = mx + viewport.xoffset + const vy = my + viewport.yoffset + return { x: vx, y: vy } +} + +export function viewport2map(vx, vy) { + const mx = vx - viewport.xoffset + const my = vy - viewport.yoffset + return { x: mx, y: my } +} + +export function setViewportCenterByWgs84(lat, lon, z) { + viewport.zoom_level = z + const size = calTileSize() + const mx = Utils.lon2x(lon, z) * size + const my = Utils.lat2y(lat, z) * size + console.log('测试', mx, my) + // const { vx, vy } = map2viewport(mx, my) + const cx = viewport.width / 2 + const cy = viewport.height / 2 + viewport.xoffset = cx - mx + viewport.yoffset = cy - my +} + +export function setViewportCenterByMap(mx, my, z) { + viewport.zoom_level = z + const cx = viewport.width / 2 + const cy = viewport.height / 2 + viewport.xoffset = cx - mx + viewport.yoffset = cy - my +} + +export function updateZoomLevel(vx, vy, newz) { + const oldz = viewport.zoom_level + const mxy = viewport2map(vx, vy) + viewport.zoom_level = newz + const scale = Math.pow(2, newz - oldz) + const newmxy = { x: mxy.x * scale, y: mxy.y * scale } + viewport.xoffset = vx - newmxy.x + viewport.yoffset = vy - newmxy.y +} + +// //test +// setViewportSize(512, 512) +// setViewportCenter(0, 0, 0) +// console.log(map2viewport(0, 0)) +// console.log(map2viewport(128, 128)) + +export function viewport2wgs84(vx, vy) { + const mlc = viewport2map(vx, vy) + const z = viewport.zoom_level + //数据超界处理, 这里对应缩放等级的地图的大小是 + const limit = calTileSize() * Math.pow(2, z) + if (vx <= 0 || vy <= 0 || vx >= limit || vy >= limit) { + throw `OutMapRange limit ${limit}` + } + const lat = Utils.tile2lat(mlc.y / 256, z) + const lon = Utils.tile2long(mlc.x / 256, z) + return { lat: lat, lon: lon } +} + +export function wgs84toviewport(lat, lon) { + //数据超界处理 经度自动限制在 -180 180 + //85.0511287798066 (2*Math.atan(Math.pow(Math.E,Math.PI))-Math.PI/2)/Math.PI*180 + const latlimit = 85.051128 + const lonp = lon - Math.floor((lon + 180) / 360) * 360 + if (lat < 0 - latlimit || lat > latlimit) { + throw 'OutMapRange' + } + const z = viewport.zoom_level + const my = Utils.lat2y(lat, z) * 256 + const mx = Utils.lon2x(lonp, z) * 256 + const vp = map2viewport(mx, my) + return { x: vp.x, y: vp.y } +} diff --git a/temp.txt b/temp.txt deleted file mode 100644 index e5769bf..0000000 --- a/temp.txt +++ /dev/null @@ -1,316 +0,0 @@ -//椭球相关参数计算 -const a = 6378137; //长半轴 a -const f = 1 / 298.257223563; //扁率 (a-b)/a -const b = a * (1 - f); //短半轴 b -const e = Math.sqrt(a * a - b * b) / a; //椭球第一偏心率 - -function degToRad(deg) { - return deg / 180 * Math.PI; -} - -function radToDeg(rad) { - return rad / Math.PI * 180; -} - -function wgs84ToWebMercator(lat, lon) { - let x = a * lon; - let y = a * Math.log(Math.tan(Math.PI / 4 + lat / 2)); - return { x: x, y: y }; -} - -function webMercatorToWgs84(x, y) { - let lon = x / a; - let lat = 2 * Math.atan(Math.pow(Math.E, y / a)) - Math.PI / 2; - return { lon: lon, lat: lat } -} - -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)))); -} - -//https://tile.openstreetmap.org/z/x/y.png -//https://tile.openstreetmap.org/15/26794/14256.png - -let cache = new Array(); - -function downloadimg(z, x, y) { - const src = 'https://tile.openstreetmap.org/' + z + '/' + x + '/' + y + '.png'; - let img = new Image(); - img.src = src; - img.onload = () => { - console.log('downloaded' + z + '/' + x + '/' + y + '.png'); - cache.push({ img: img, z: z, x: x, y: y }); - } -} - -function getIndex(z, x, y) { - for (let i = 0; i < cache.length; i++) { - const e = cache[i]; - if (e.z == z && e.x == x && e.y == y) { - return i; - } else { - continue; - } - } - //last - downloadimg(z, x, y); - return cache.length; -} - -//已知条件 lat long zoom_level 显示范围的宽高 -let zoom_level = 15; -let lat = 22.7490073; -let long = 114.3781213; -let width = 1600; -let height = 900; - -function calBasetile(lat, long, zoom_level) { - let basetile = {}; - basetile.x = lon2x(long, zoom_level); //对应zoomlevel下long的x坐标 - basetile.y = lat2y(lat, zoom_level); //对应zoomlevel下lat的y坐标 - basetile.tilex = lon2tile(long, zoom_level); //对应zoomlevel下long的x tile编号 - basetile.tiley = lat2tile(lat, zoom_level); //对应zoomlevel下lat的y tile编号 - basetile.offsetx = Math.floor((basetile.x - basetile.tilex) * 256); //相对于左上角,其点的x偏移值 - basetile.offsety = Math.floor((basetile.y - basetile.tiley) * 256); //相对于左上角,其点的y偏移值 - - basetile.minx = width / 2 - basetile.offsetx; - basetile.maxx = basetile.minx + 256; - basetile.miny = height / 2 - basetile.offsety; - basetile.maxy = basetile.miny + 256; - - basetile.p1 = Math.floor(basetile.maxy / 256 + 0.5); - basetile.p2 = Math.floor((height - basetile.miny) / 256 + 0.5); - basetile.p3 = Math.floor(basetile.maxx / 256 + 0.5); - basetile.p4 = Math.floor((width - basetile.minx) / 256 + 0.5); - console.log(basetile); - return basetile; -} - -//基于已知条件计算需要的各个tile -function needtiles(basetile, p1, p2, p3, p4) { - let tiles = new Array(); - let tilex = basetile.tilex; - let tiley = basetile.tiley; - tiles.push({ x: tilex, y: tiley }); - for (let i = 0; i < p4; i++) { - tiles.push({ x: tilex + i + 1, y: tiley }); - } - for (let i = 0; i < p3; i++) { - tiles.push({ x: tilex - i - 1, y: tiley }); - } - for (let i = 0; i < p1; i++) { - for (let j = 0; j < p3 + p4 + 1; j++) { - tiles.push({ x: tilex - p3 + j, y: tiley - i - 1 }); - } - } - for (let i = 0; i < p2; i++) { - for (let j = 0; j < p3 + p4 + 1; j++) { - tiles.push({ x: tilex - p3 + j, y: tiley + i + 1 }); - } - } - //console.log(tiles); - //console.log(tiles.length); - return tilesfilter(tiles, zoom_level); -} - -//过滤超过限制的tiles -function tilesfilter(tiles, zoom_level) { - const maxrange = Math.pow(2, zoom_level) - 1; - let newtiles = new Array(); - for (let i = 0; i < tiles.length; i++) { - if (tiles[i].x < 0 || tiles[i].x > maxrange) { - continue; - } - if (tiles[i].y < 0 || tiles[i].y > maxrange) { - continue; - } - newtiles.push(tiles[i]); - } - return newtiles; -} - -let basetile = calBasetile(lat, long, zoom_level); -let tiles = needtiles(basetile, basetile.p1, basetile.p2, basetile.p3, basetile.p4); - - -//渲染问题 -//通过与基准之间的位置关系计算渲染的坐标 -//minx miny 即为基准tile当前渲染位置 - -// for (let i = 0; i < tiles.length; i++) { -// const element = tiles[i]; -// let renderx = (element.x - tilex) * 256 + minx; -// let rendery = (element.y - tiley) * 256 + miny; -// console.log({ x: renderx, y: rendery }); -// } - -let canvas = document.getElementById('map'); -canvas.width = map.clientWidth; -canvas.height = map.clientHeight; -let ctx = canvas.getContext('2d'); - -function clearcanvas() { - ctx.clearRect(0, 0, canvas.width, canvas.height); -} - -function drawPoint(x, y) { - ctx.fillStyle = "white" - ctx.strokeStyle = "#a35312" - ctx.beginPath(); - ctx.arc(x, y, 3, 0, Math.PI * 2, true); - ctx.closePath(); - ctx.fill(); - ctx.stroke(); -} - -function drawRect(x, y) { - ctx.strokeStyle = "white" - ctx.strokeRect(x, y, 256, 256); -} - -function drawImg(z, tilex, tiley, x, y, ) { - const index = getIndex(z, tilex, tiley); - if (cache[index]) { - ctx.drawImage(cache[index].img, x, y); - } -} - -function drawtext(text, x, y) { - ctx.fillStyle = "white" - ctx.font = "18px serif"; - ctx.textAlign = "center"; - ctx.textBaseline = "middle"; - ctx.fillText(text, x, y); -} - -function drawgrid(basetile, tiles) { - let minx = basetile.minx; - let miny = basetile.miny; - let offsetx = basetile.offsetx; - let offsety = basetile.offsety; - let tilex = basetile.tilex; - let tiley = basetile.tiley; - clearcanvas(); - drawPoint(minx + offsetx, miny + offsety); - for (let i = 0; i < tiles.length; i++) { - const element = tiles[i]; - let renderx = (element.x - tilex) * 256 + minx; - let rendery = (element.y - tiley) * 256 + miny; - //console.log({ x: renderx, y: rendery }); - drawPoint(renderx, rendery); - drawRect(renderx, rendery); - drawImg(zoom_level, tiles[i].x, tiles[i].y, renderx, rendery); - const showtext = 'x:' + element.x + ' ' + 'y:' + element.y; - drawtext(zoom_level, renderx + 256 / 2, rendery + 256 / 2); - drawtext(showtext, renderx + showtext.length / 4 * 18 + 4, rendery + 9); - } -} - -drawgrid(basetile, tiles); - -// drawPoint(width / 2, height / 2); - -// const element = tiles[0]; -// let renderx = (element.x - tilex) * 256 + minx; -// let rendery = (element.y - tiley) * 256 + miny; -// drawPoint(renderx, rendery); -// drawPoint(renderx + 256 / 2, rendery + 256 / 2); -// drawRect(renderx, rendery); - -// drawtext(showtext, renderx + 256 / 2, rendery + 256 / 2); -// console.log({ x: renderx, y: rendery }); - -//实现移动缩放方法 -//缩放 -map.onwheel = (e) => { - //console.log(e); - const zoomleveloffset = (e.deltaY > 0) ? 1 : -1; - zoom_level -= zoomleveloffset; - if (zoom_level < 0) { - zoom_level = 0; - return; - } - - if (zoom_level > 19) { - zoom_level = 19; - return; - } - console.log(zoom_level); - //clearcanvas(); - //重新计算basetile - basetile = calBasetile(lat, long, zoom_level); - tiles = needtiles(basetile, basetile.p1, basetile.p2, basetile.p3, basetile.p4); - drawgrid(basetile, tiles); -} - -let moveflag = false; -let downpoing = { x: 0, y: 0 }; - -map.onmousedown = (e) => { - console.log('鼠标按下'); - moveflag = true; - downpoing.x = e.offsetX; - downpoing.y = e.offsetY; -} - -map.onmouseup = (e) => { - console.log('鼠标松开'); - moveflag = false; -} - -map.onmousemove = (e) => { - if (moveflag) { - //console.log('有效位移'); - const movex = downpoing.x - e.offsetX; - const movey = downpoing.y - e.offsetY; - //实现移动逻辑 - let xp = basetile.x + movex / 256; //注意像素移动要除以256 - let yp = basetile.y + movey / 256; - long = tile2long(xp, zoom_level); - let latp = tile2lat(yp, zoom_level); - //需要在y方向限制移动范围 - //(2*Math.atan(Math.pow(Math.E,Math.PI))-Math.PI/2)/Math.PI*180 - //85.0511287798066 - if (Math.abs(latp) > 89.99999) return; - // if (lat2y(latp, zoom_level) == Infinity || lat2y(latp, zoom_level) == NaN) { - // console.log('outrange'); - // return; - // } - lat = latp; - console.log(long + ' ' + lat); - basetile = calBasetile(lat, long, zoom_level); - tiles = needtiles(basetile, basetile.p1, basetile.p2, basetile.p3, basetile.p4); - drawgrid(basetile, tiles); - - downpoing.x = e.offsetX; - downpoing.y = e.offsetY; - } -} - -// - diff --git a/test.js b/test.js deleted file mode 100644 index 5df298e..0000000 --- a/test.js +++ /dev/null @@ -1,251 +0,0 @@ -let map = document.getElementById('map'); -map.width = map.clientWidth; -map.height = map.clientHeight; -let ctx = map.getContext('2d'); - -const tilesize = 256; -const minlevel = 0; -const maxlevel = 19; -let zoom_level = 0; //默认 - -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 xhrmanager = 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(); - xhrabort(); - 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); - //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); - }); -} - -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(viewport2mplat(e.offsetX, e.offsetY)); -} -map.onmouseup = (e) => { - console.log('鼠标松开'); - moveflag = false; -} -map.onmousemove = (e) => { - 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(); - -//add opeenstreetmap -function downloadimg(z, x, y) { - return new Promise((resolve, reject) => { - const url = `https://tile.openstreetmap.org/${z}/${x}/${y}.png`; - let img = new Image(); - img.onload = () => { - console.log('downloaded' + z + '/' + x + '/' + y + '.png'); - cache.push({ img: img, z: z, x: x, y: y }); - resolve(img); - } - img.src = url; - }); -} - -//尝试优化图片下载机制 -function xhrdownloadimg(z, x, y) { - return new Promise((resolve, reject) => { - const url = `https://tile.openstreetmap.org/${z}/${x}/${y}.png`; - let imgxhr = new XMLHttpRequest(); - imgxhr.open('GET', url, true); - //imgxhr.withCredentials = true; - imgxhr.responseType = 'blob'; - imgxhr.onload = function (e) { - if (this.status === 200 && imgxhr.response) { - let blob = new Blob([imgxhr.response], { type: 'image/png' }); - let img = createImageBitmap(blob); - cache.push({ img: img, z: z, x: x, y: y }); - resolve(img); - } - }; - xhrmanager.push(imgxhr); - imgxhr.send(null); - }); -} - -function xhrabort() { - xhrmanager.forEach(element => { - element.abort(); - }); - xhrmanager = new Array(); -} - -async function getImg(z, x, y) { - const found = cache.find(e => (e.z == z && e.x == x && e.y == y)); - if (found == undefined) { - let p = await xhrdownloadimg(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); - } - }); -} \ No newline at end of file diff --git a/update.7z b/update.7z deleted file mode 100644 index 29b5240..0000000 Binary files a/update.7z and /dev/null differ diff --git a/viewport.js b/viewport.js deleted file mode 100644 index 034bae8..0000000 --- a/viewport.js +++ /dev/null @@ -1,437 +0,0 @@ -//help array; -let Caches = new Array(); //cache -//let downloadRecord = new Array(); -let dm = new Array(); //尝试增加下载管理 downloadmanager - -let alltilecount = 0; -let downtilecount = 0; - - -function ViewPort(canvasId, options) { - // allow without 'new' - if (!(this instanceof ViewPort)) return new ViewPort(canvasId, options); - this.TILESIZE = 256; //实际的底图大小 - //init - this.canvas = document.getElementById(canvasId); - this.canvas.width = this.canvas.clientWidth; - this.canvas.height = this.canvas.clientHeight; - - this.ctx = this.canvas.getContext('2d'); - this.width = this.canvas.clientWidth; - this.height = this.canvas.clientHeight; - if (!options) options = {}; //options could be null or undefine - this.minZoomLevel = options.minZoomLevel ?? 0; - this.maxZoomLevel = options.maxZoomLevel ?? 21; - this.zoomLevel = options.zoomLevel ?? 0; - this.dzl = options.dzl ?? 0.25; // zoomlevel offset value - this.rtSize = calTileSize(this.TILESIZE, this.zoomLevel); //named rtSize because real time tile size - - const defaultCenterX = Math.pow(2, this.zoomLevel) * this.TILESIZE / 2; - const defaultCenterY = Math.pow(2, this.zoomLevel) * this.TILESIZE / 2; - this.xoffset = this.width / 2 - defaultCenterX; - this.yoffset = this.height / 2 - defaultCenterY; - - //如果在options中有指定地图中心点 - //options.centerLng && options.centerLat - if (options.centerLat != null && options.centerLng != null && - !isNaN(options.centerLat) && !isNaN(options.centerLng)) { - console.log('指定中心点'); - try { - ({ lat: lat, lng: lng } = this.latlngPreCheck(options.centerLat, options.centerLng)); - const my = lat2y(lat, this.zoomLevel) * this.TILESIZE; - const mx = lon2x(lng, this.zoomLevel) * this.TILESIZE; - this.xoffset = this.width / 2 - mx; - this.yoffset = this.height / 2 - my; - //console.log('check ', this.viewport2Latlng(this.width / 2, this.height / 2)); - } catch (error) { - console.log('使用默认中心点'); - } - } - - this.needtiles = new Array(); //需要加载的底图 实时变化 - - //move - let moveflag = false; - let downpoing = { x: 0, y: 0 }; - this.canvas.onmousedown = (e) => { - moveflag = true; - [downpoing.x, downpoing.y] = [e.offsetX, e.offsetY]; - //坐标转换 debug - //console.log(this.zoomLevel, e.offsetX, e.offsetY); - const wgs84 = this.viewport2Latlng(e.offsetX, e.offsetY); - console.log('clicked', wgs84); - } - this.canvas.onmouseup = (e) => { - moveflag = false; - } - this.canvas.onmouseout = () => { - moveflag = false; //从元素范围外移动回来时不会继续移动,必须松开左键,按下左键后再进行移动 - } - this.canvas.onmousemove = (e) => { - if (moveflag) { - const movex = downpoing.x - e.offsetX; - const movey = downpoing.y - e.offsetY; - //实现移动逻辑 - this.xoffset -= movex; - this.yoffset -= movey; - - this.drawTiles(); - downpoing.x = e.offsetX; - downpoing.y = e.offsetY; - } - } - - //scale - this.canvas.onwheel = (e) => { - const dzl = this.dzl; - const zoomleveloffset = (e.deltaY > 0) ? 1 * dzl : -1 * dzl; - const oldz = this.zoomLevel; - this.zoomLevel = this.zoomLevel - zoomleveloffset; - if (this.zoomLevel < this.minZoomLevel) { - this.zoomLevel = this.minZoomLevel; - return; - } - - if (this.zoomLevel > this.maxZoomLevel) { - this.zoomLevel = this.maxZoomLevel; - return; - } - //console.log(this.zoomLevel, e.offsetX, e.offsetY); - this.updateViewport(e.offsetX, e.offsetY, oldz, this.zoomLevel); - this.drawTiles(); - } - - this.drawTiles(); - - // setInterval(() => { - // debug(); - // }, 1000); -} - -//扩展一下Array 用以辅助进行加载底图 -Array.prototype.remove = function (val) { - const index = this.indexOf(val); - if (index > -1) { - return this.splice(index, 1); - } - return this; -} - -ViewPort.prototype = { - latlngPreCheck(lat, lng) { - //数据超界处理 经度 -180 180 - //纬度 < 85.0511287798066 (2*Math.atan(Math.pow(Math.E,Math.PI))-Math.PI/2)/Math.PI*180 - const latlimit = 85.0511287798066; //取近似值 - const lngp = lng - Math.floor((lng + 180) / 360) * 360; - if (lat < (0 - latlimit) || lat > latlimit) { - throw "OutMapRange"; - } - return { lat: lat, lng: lngp }; - }, - - mplatPreCheck(mx, my) { - //数据超界处理, 这里是对应缩放等级的地图的大小,底图大小*底图块数 注意底图块数始终为整数level时的底图块数 - //实际只改变的底图大小,没有改变块数 - let limit = calTileSize(this.TILESIZE, this.zoomLevel) * Math.pow(2, this.zoomLevel); - if (this.zoomLevel != Math.floor(this.zoomLevel)) { - limit = calTileSize(this.TILESIZE, this.zoomLevel) * Math.pow(2, Math.floor(this.zoomLevel + 1)); - } - if (mx <= 0 || my <= 0 || mx >= limit || my >= limit) { - throw "OutMapRange"; - } - return { x: mx, y: my }; - }, - - //------------------------------------- - //这几个函数是坐标转换函数 - mplat2Viewport(mx, my) { - const vx = mx + this.xoffset; - const vy = my + this.yoffset; - return { x: vx, y: vy }; - }, - - viewport2Mplat(vx, vy) { - const mx = vx - this.xoffset; - const my = vy - this.yoffset; - return { x: mx, y: my }; - }, - - viewport2Latlng(vx, vy) { - let mplatc = this.viewport2Mplat(vx, vy); //mplat coordinates - mplatc = this.mplatPreCheck(mplatc.x, mplatc.y); - const lat = tile2lat(mplatc.y / this.TILESIZE, this.zoomLevel); - const lng = tile2long(mplatc.x / this.TILESIZE, this.zoomLevel); - return { lat: lat, lng: lng } - }, - latlng2Viewport(lat, lng) { - ({ lat: lat, lng: lng } = this.latlngPreCheck(lat, lng)); //尝试使用解构赋值 - const my = lat2y(lat, this.zoomLevel) * this.TILESIZE; - const mx = lon2x(lng, this.zoomLevel) * this.TILESIZE; - const vp = this.mplat2Viewport(mx, my); - return { x: vp.x, y: vp.y }; - }, - //---------------------------------------- - - //更新偏移值 - updateViewport(scrollx, scrolly, oldz, newz) { - //计算出缩放后坐标系的偏移值 - // xv = xm + offset ; offset = xv - xm - const scrollm = this.viewport2Mplat(scrollx, scrolly); - const basescale = Math.pow(2, newz - oldz); - const newscrollm = { x: scrollm.x * basescale, y: scrollm.y * basescale }; - this.xoffset = scrollx - newscrollm.x; - this.yoffset = scrolly - newscrollm.y; - }, - - //过滤需要加载的底图 - tilesFilter(tails) { - let news = new Array(); - let max = Math.pow(2, this.zoomLevel); - if (this.zoomLevel != Math.floor(this.zoomLevel)) { - max = Math.pow(2, Math.floor(this.zoomLevel + 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; - }, - - - //一些绘制函数 - clearCanvas() { - this.ctx.clearRect(0, 0, this.width, this.height); - }, - drawRect(x, y, width, height) { - this.ctx.strokeStyle = "white" - this.ctx.strokeRect(x, y, width, height); - }, - drawtext(text, x, y) { - this.ctx.fillStyle = "white" - this.ctx.font = "18px serif"; - this.ctx.textAlign = "center"; - this.ctx.textBaseline = "middle"; - this.ctx.fillText(text, x, y); - }, - drawTiles() { - this.clearCanvas(); - const viewport_left_top = this.viewport2Mplat(0, 0); - const viewport_right_bottom = this.viewport2Mplat(this.width, this.height); - const mixnum = calTileNum(viewport_left_top.x, this.TILESIZE, this.zoomLevel); - const miynum = calTileNum(viewport_left_top.y, this.TILESIZE, this.zoomLevel); - const maxnum = calTileNum(viewport_right_bottom.x, this.TILESIZE, this.zoomLevel); - const maynum = calTileNum(viewport_right_bottom.y, this.TILESIZE, this.zoomLevel); - const horicount = maxnum - mixnum + 1; - const verticount = maynum - miynum + 1; - 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 }); - } - } - this.needtiles = this.tilesFilter(tails); //获取当前视口要渲染的底图 - let z = this.zoomLevel; //这里的几行代码的逻辑是用到了5个函数里面,后面可以想一下怎么重构 - if (this.zoomLevel != Math.floor(this.zoomLevel)) { - z = Math.floor(this.zoomLevel + 1); - } - - //尝试在这里移除不需要继续下载的内容 - let indexs = new Array(); - dm.forEach(d => { - if (d.z !=z) { - if(this.needtiles.find(e=>e.x==d.x&&e.y==d.y)==undefined){ - d.xhr.abort(); - console.log('中断请求'); - dm.remove(d); - } - } - }); - - - - alltilecount += this.needtiles.length; //for debug - //console.log(this.needtiles); - this.needtiles.forEach(et => { - const etsize = calTileSize(this.TILESIZE, this.zoomLevel); - const tilev = this.mplat2Viewport(et.x * etsize, et.y * etsize); - this.drawRect(tilev.x, tilev.y, etsize, etsize); - const text = `${z}/${et.x}/${et.y}.png`; - // console.log(text); - this.drawtext(text, tilev.x + etsize / 2, tilev.y + etsize / 2); - this.drawTileImg(z, et.x, et.y); - }); - //debug(); - }, - drawTileImg(z, x, y) { - - const img = getImg(this, z, x, y); - img.then((e) => { - if (isTileShouldDownload(this, z, x, y)) { - //对e添加判断,似乎不是每一个e都是一个图片 - if (e instanceof ImageBitmap) { - const etsize = calTileSize(this.TILESIZE, this.zoomLevel); - const tilev = this.mplat2Viewport(x * etsize, y * etsize); - this.ctx.drawImage(e, tilev.x, tilev.y, etsize, etsize); - } - } - }).catch((e) => { - console.log(e); - }); - }, - -} - -//计算实时底图大小 -function calTileSize(tilesize, zoomLevel) { - let rtSize = tilesize; - if (zoomLevel != Math.floor(zoomLevel)) { - rtSize = tilesize * Math.pow(2, zoomLevel - Math.floor(zoomLevel + 1)); - } - return rtSize; -} - -//计算当前坐标值所在的底图编号 -function calTileNum(coord, tilesize, zoomLevel) { - return Math.floor(coord / calTileSize(tilesize, zoomLevel)); -} - -//=====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)))); -} - -//=========下载图片相关================== -//将其和viewpoint分离,方便后续修复问题和增加新的下载方式或者底图源 - -//判断一张底图需不需要继续下载,这里采用保守一点的下载策略,必须是先判定为需要加载的底图才开始下载 -//传入vp以获取相关参数 -function isTileShouldDownload(vp, z, x, y) { - let fz = vp.zoomLevel; - if (vp.zoomLevel != Math.floor(vp.zoomLevel)) { - fz = Math.floor(vp.zoomLevel + 1); - } - if (vp.needtiles.find(t => z == fz && t.x == x && t.y == y)) { - return true; - } - return false; -} - -//add autonavi -function downloadimg(z, x, y) { - return new Promise((resolve) => { - //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 }; - downloadRecord.push(obj); //下载前加入记录 - let img = new Image(); - const dmobj = { img: img, z: z, x: x, y: y }; - img.onload = () => { - //console.log('downloaded' + z + '/' + x + '/' + y + '.png'); - downloadRecord.remove(obj); //下载完成后从记录中移除 - dm.remove(dmobj); - Caches.push({ img: img, z: z, x: x, y: y }); //下载完成后加入缓存 - downtilecount += 1; - resolve(img); - } - img.src = url; - dm.push(dmobj); //尝试添加管理 - }); -} - -//尝试优化图片下载机制 //采用下载速度比较慢的openstreetmap 测试 -function xhrdownloadimg(z, x, y) { - return new Promise((resolve, reject) => { - const url = `https://b.tile.openstreetmap.org/${z}/${x}/${y}.png`; - let imgxhr = new XMLHttpRequest(); - imgxhr.open('GET', url, true); - - //add downloadcheck - //const obj = { z: z, x: x, y: y }; - //downloadRecord.push(obj); //下载前加入一条记录 - const dmobj = { xhr: imgxhr, z: z, x: x, y: y }; - - //imgxhr.withCredentials = true; - imgxhr.responseType = 'blob'; - imgxhr.onload = function (e) { - if (this.status === 200 && imgxhr.response) { - let blob = new Blob([imgxhr.response], { type: 'image/png' }); - let img = createImageBitmap(blob); - Caches.push({ img: img, z: z, x: x, y: y }); - downtilecount += 1; - //downloadRecord.remove(obj); //下载完成后将记录移除 - dm.remove(dmobj); - console.log('dm下载完成减少'); - resolve(img); - } else { - reject('下载似乎失败了'); - dm.remove(dmobj); - console.log('dm下载失败减少'); - } - }; - dm.push(dmobj); //保存xhr对象 和xyz索引 - imgxhr.send(null); - }); -} - -async function getImg(vp, z, x, y) { - const found = Caches.find(e => (e.z == z && e.x == x && e.y == y)); //查找缓存 - if (found == undefined) { //缓存中没有 - console.log('缓存中没有' + z + '/' + x + '/' + y + '.png'); - //这里不需要判断是否继续加载,在还未开始下载图片前,这里可以说是立即执行的 - console.log(isTileShouldDownload(vp, z, x, y));//always true - const dmobj = dm.find(e => (e.z == z && e.x == x && e.y == y)); - if (dmobj == undefined) { - //如果缓存中没有,并且没有下载记录 发起下载请求并等待结果 - let p = await xhrdownloadimg(z, x, y); - return p; - } - else {//有下载记录就不用继续下载了 - console.log(vp.zoomLevel, vp.xoffset, vp.yoffset); - return Promise.reject('需要等待下载完成'); - } - } else { //缓存中有直接返回 - console.log('缓存中有' + z + '/' + x + '/' + y + '.png'); - return found.img; - } -} - -function debug() { - console.log('=============debug================='); - console.log('alltilecount', alltilecount); - console.log('downtilecount', downtilecount); - console.log('cachecount', Caches.length); - console.log('dm count', dm.length); - console.log('=============debug================='); -} \ No newline at end of file