You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

317 lines
9.1 KiB
Plaintext

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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