//椭球相关参数计算 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; } }