(() => {
// 初始地图
const initMap = (map) => {
return {
map,
async loadImage(args) {
const { file, minWidth, minHeight } = args
args.img = args.img
? args.img
: await blobImageLoad(file, minWidth, minHeight)
return loadImageLayer(map, args)
},
screenToLatlan({x, y}) {
const real = map.getCoordinateFromPixel([x, y])
const latlan = ol.proj.transform(real,'EPSG:3857','EPSG:4326')
return latlan
}
}
}
const loadImageLayer = (map, args) => {
const {
lon,
lat
} = args
const itude = ol.proj.fromLonLat([lon, lat])
const { image: imageLayer, canvas } = loadImage(map, args, itude)
map.addLayer(imageLayer);
map.getView().setCenter(
ol.proj.fromLonLat([lon, lat])
);
map.getView().setZoom(19)
return canvas
}
// 经纬度转canvas坐标
const itudeToCanvasPos = (map, extent, itude) => {
//Canvas四至范围不同于当前地图四至范围,计算出南北方向与东西方向的偏移
const mapExtent = map.getView()
.calculateExtent(map.getSize())
//当前底图视图范围的投影坐标
const canvasOrigin = map.getPixelFromCoordinate(
[ extent[0], extent[3] ]
);
//添加到地图上的canvas图像的左上角
const mapOrigin = map.getPixelFromCoordinate(
[ mapExtent[0], mapExtent[3] ]
);
const delta = [
mapOrigin[0] - canvasOrigin[0],
mapOrigin[1] - canvasOrigin[1]
];
const leftTop = map.getPixelFromCoordinate(itude)
return {
x: leftTop[0] + delta[0],
y: leftTop[1] + delta[1]
}
}
// 平移,旋转,放大当前canvas
const transformCanvasCall = (
canvas,
transform,
oper,
center = {
x: 0,
y: 0
}
) => {
const ctx = canvas.getContext('2d')
const {
translate,
scale,
rotate
} = transform
ctx.translate(center.x, center.y)
translate && ctx.translate(translate.x, translate.y)
rotate && ctx.rotate(rotate * (Math.PI / 180))
scale && ctx.scale(scale[0], scale[1])
oper && oper()
// scale && ctx.scale(1 / scale, 1 / scale)
// rotate && ctx.rotate(-rotate * (Math.PI / 180))
// translate && ctx.translate(-translate.x, -translate.y)
ctx.translate(-center.x, -center.y)
}
const genImgCanvasItudeToReal = (map, canvas, extent) =>
(itude) => {
return genImgCanvasPosToReal(map, canvas)(
itudeToCanvasPos(map, extent, itude)
)
}
const genImgCanvasPosToReal = (map, canvas) =>
(pos) => {
const $real = map.getViewport()
const offsetWidth = (canvas.width - $real.offsetWidth) / 2
const offsetHeight = (canvas.height - $real.offsetHeight) / 2
return {
x: pos.x - offsetWidth,
y: pos.y - offsetHeight
}
}
const genImgCanvasTransfrom = (canvas, arrayImgs, scale, initPos) =>
(transform) => {
const ctx = canvas.getContext('2d');
const dscale = transform.scale || [1, 1]
const resize = 1 / (scale * 10)
const doScale = [
resize * dscale[0],
resize * dscale[1]
]
const imgData = { width: 0, height: 0 }
arrayImgs.forEach(imgs => {
let height = 0
imgs.forEach(([img]) => height += img.height)
imgData.width += imgs[0][0].width
if (imgData.height < height) {
imgData.height = height
}
})
initPos.x -= imgData.width / 2
initPos.y -= imgData.height / 2
// , translate: { x: -(imgData.width / 2) * doScale[0], y: -(imgData.height / 2) * doScale[1] }
ctx.fillStyle = 'rgba(0,0,0,0.1)'
ctx.fillRect(0,0, canvas.width, canvas.height)
transformCanvasCall(
canvas,
{ ...transform, scale: doScale },
() => {
transform.draw && transform.draw(ctx)
let width = 0
arrayImgs.forEach(imgs => {
let height = 0
imgs.forEach(([img]) => {
ctx.drawImage(img, width, height)
height += img.height
})
width += imgs[0][0].width
})
},
transform.center
)
const move = {
x: transform.translate.x - initPos.x,
y: transform.translate.y - initPos.y,
}
const start = {
x: initPos.x + move.x,
y: initPos.y + move.y,
}
const end = {
x: start.x + imgData.width * doScale[0],
y: start.y + imgData.height * doScale[1],
}
canvas.position = [
start,
end,
Math.abs(start.x - end.x) / resize,
Math.abs(start.y - end.y) / resize
]
canvas.resize = resize
canvas.imgData = imgData
canvas.imgBox = [
canvas.posToReal(start),
canvas.posToReal(end),
Math.abs(start.x - end.x),
Math.abs(start.y - end.y)
]
}
// 加载url
const loadImage = (map, args, itude) => {
const canvas = document.createElement('canvas');
const imageCanvas = new ol.source.ImageCanvas({
canvasFunction(extent, scale, _2, size) {
const pos = itudeToCanvasPos(map, extent, itude)
const imgData = { width: 0, height: 0 }
args.img.forEach(imgs => {
let height = 0
imgs.forEach(([img]) => height += img.height)
imgData.width += imgs[0][0].width
if (imgData.height < height) {
imgData.height = height
}
})
pos.x -= imgData.width / 2 * scale
pos.y -= imgData.height / 2 * scale
canvas.width = size[0];
canvas.height = size[1]
canvas.posToReal = genImgCanvasPosToReal(map, canvas);
canvas.transform = genImgCanvasTransfrom(canvas, args.img, scale, pos, imageCanvas);
canvas.itudeToReal = genImgCanvasItudeToReal(map, canvas, extent)
canvas.transform({
...args,
translate: {
x: (args.translate ? args.translate.x : 0) + pos.x,
y: (args.translate ? args.translate.y : 0) + pos.y
}
})
return canvas;
}
})
const image = new ol.layer.Image({ source: imageCanvas })
canvas.imageLayer = imageCanvas
return {
image,
canvas
}
}
// 返回本地url
const blobImageLoad = (arrayImages, minWidth, minHeight) => {
console.log(arrayImages)
const analysis = (blob) => new Promise((resolve, reject) => {
const url = window.URL.createObjectURL(blob);
const img = new Image()
img.onload = () => {
if (img.width < minWidth || img.height < minHeight) {
reject('图片宽高需要大于512')
} else {
resolve([img, url, blob])
}
}
img.src = url
})
let arrasPromises = []
for (let images of arrayImages) {
let analys = []
for (let bolb of images) {
analys.push(analysis(bolb))
}
arrasPromises.push(
Promise.all(analys)
)
}
return Promise.all(arrasPromises)
}
// 获取逆转矩阵
const getCanvasInverImatrix = $canvas => {
const ctx = $canvas.getContext('2d')
const transform = ctx.getTransform()
return transform.invertSelf();
}
// canvas坐标转屏幕坐标
const getCanvasToScreenPos = ($canvas, {x, y}) => {
const {
a, b, c,
d, e, f
} = getCanvasInverImatrix($canvas)
const screenX = (c * y - d * x + d * e - c * f) / (b * c - a * d)
const screenY = (y - screenX * b - f) / d
return {
x: Math.round(screenX),
y: Math.round(screenY),
}
}
// 屏幕坐标转canvas坐标
const getScreenToCanvasPos = ($canvas, {x, y}) => {
const {
a, b, c,
d, e, f
} = getCanvasInverImatrix($canvas)
return {
x: Math.round(x * a + y * c + e),
y: Math.round(x * b + y * d + f)
};
}
Vue.component('imageTranform', {
props: ['mapOl'],
name: 'imageTranform',
template: `
`,
data() {
return {
isHover: false,
box: {},
left: 0,
top: 0
}
},
methods: {
async imageChange(e) {
let imagesArray = []
if (e.target.files.length > 1) {
const formatError = () => {
e.target.value = null
console.log(imagesXYZ)
alert('目录不规范 请上传 z/x/y.png 格式目录,且在最底级目录放置图片文件')
}
let imagesXYZ = {}
for (let file of e.target.files) {
let locals = file.webkitRelativePath.split(/[\\|//]/)
if (locals.length < 3) return formatError()
let current = imagesXYZ
for (let i = 0; i < locals.length; i++) {
let dir = locals[i]
if (i !== locals.length - 1) {
if (!current[dir]) {
current[dir] = i === locals.length - 2 ? [] : {}
}
current = current[dir]
if (i === locals.length - 3) {
current.key = 'z'
}
}
if (i === locals.length - 1) {
current.push(file)
}
}
}
(function analysis (updateXYZ) {
if (updateXYZ.key === 'z') {
imagesXYZ = updateXYZ
return;
}
const names = Object.keys(updateXYZ).sort((a, b) => b - a)
names.forEach(key => {
if (key !== names[0]) {
delete updateXYZ[key]
}
})
analysis(updateXYZ[names[0]])
})(imagesXYZ);
if (!(imagesXYZ && imagesXYZ.key === 'z' && !Array.isArray(imagesXYZ))) {
return formatError()
}
for (let key in imagesXYZ) {
if (!Array.isArray(imagesXYZ[key]) && key !== 'key') {
return formatError()
}
}
delete imagesXYZ.key
Object.keys(imagesXYZ).sort((a, b) => a - b).forEach(key => {
imagesArray.push(
imagesXYZ[key].sort((a, b) => parseInt(a.name) - parseInt(b))
)
})
} else {
imagesArray = [[e.target.files[0]]]
}
try {
this.transfroms = []
this.args = {
draw: (ctx) => {
this.transfroms.forEach(transform => {
transform.forEach(({translate, scale, rotate, center}) => {
// 设置绘制颜色
center && ctx.translate(center.x, center.y)
translate && ctx.translate(translate.x, translate.y)
rotate && ctx.rotate(rotate * (Math.PI / 180))
scale && ctx.scale(scale[0], scale[1])
center && ctx.translate(-center.x, -center.y)
// if (center) {
// ctx.fillStyle = "geend";
// // 绘制成矩形
// ctx.fillRect(center.x, center.y, 100, 100);
// }
})
})
setTimeout(() => {
this.updateBox(this.imgCanvas.imgBox)
})
},
file: imagesArray,
lon: 113.59963069739054,
lat: 22.364821730960752,
translate: {x: 0, y: 0},
scale: [1, 1],
direction: 0
}
console.log('---0---')
this.imgCanvas = await this.map.loadImage(this.args)
} catch(e) {
alert(e)
}
},
updateBox() {
const calcPos = pos => getCanvasToScreenPos(this.imgCanvas, pos)
this.box = {
tl: this.imgCanvas.posToReal(calcPos({x: 0, y: 0})),
tc: this.imgCanvas.posToReal(calcPos({x: this.imgCanvas.imgData.width / 2, y: 0})),
tr: this.imgCanvas.posToReal(calcPos({x: this.imgCanvas.imgData.width, y: 0})),
rc: this.imgCanvas.posToReal(calcPos({x: this.imgCanvas.imgData.width, y: this.imgCanvas.imgData.height / 2})),
lc: this.imgCanvas.posToReal(calcPos({x: 0, y: this.imgCanvas.imgData.height / 2})),
br: this.imgCanvas.posToReal(calcPos({x: this.imgCanvas.imgData.width, y: this.imgCanvas.imgData.height})),
bl: this.imgCanvas.posToReal(calcPos({x: 0, y: this.imgCanvas.imgData.height})),
bc: this.imgCanvas.posToReal(calcPos({x: this.imgCanvas.imgData.width / 2, y: this.imgCanvas.imgData.height})),
cc: this.imgCanvas.posToReal(calcPos({x: this.imgCanvas.imgData.width / 2, y: this.imgCanvas.imgData.height / 2})),
}
let maxX = this.box.tl.x
let minX = this.box.tl.x
let maxY = this.box.tl.y
let minY = this.box.tl.y
Object.values(this.box).forEach(({x, y}) => {
x > maxX && (maxX = x)
y > maxY && (maxY = y)
x < minX && (minX = x)
y < minY && (minY = y)
})
this.box.width = Math.abs(maxX - minX)
this.box.height = Math.abs(maxY - minY)
},
mapStartHandle() {
this.mapDown = true
},
moveHandle(e) {
if (!this.imgCanvas || !this.imgCanvas.imgBox) {
return;
}
if (this.moveing && this.oper) {
this.move(e)
} else {
this.mapDown && this.imgCanvas.imageLayer.refresh()
// const [start, end] = this.box
// this.isHover = e.clientX > start.x && e.clientX < end.x &&
// e.clientY > start.y && e.clientY < end.y
}
},
startMove(ev, oper, dir) {
this.startTransform = {
...this.args
}
this.transfroms.push([])
this.moveing = true
this.oper = oper
this.dir = dir
this.startMovePos = {
x: ev.clientX,
y: ev.clientY
}
},
move(ev) {
if (!this.moveing) return;
const transfrom = this.transfroms[this.transfroms.length - 1]
const start = getScreenToCanvasPos(
this.imgCanvas,
this.startMovePos
)
const end = getScreenToCanvasPos(
this.imgCanvas,
{ x: ev.clientX, y: ev.clientY }
)
const move = {
x: end.x - start.x,
y: end.y - start.y
}
if (this.oper === 'move') {
transfrom.push({ translate: move })
} else if (this.oper === 'scale'){
const width = this.imgCanvas.position[2]
const height = this.imgCanvas.position[3]
let xScale, yScale
switch(this.dir) {
case 'tl':
xScale = (width - move.x) / width
yScale = (height - move.y) / height
if (xScale > 0.1 && yScale > 0.1) {
transfrom.push({
scale: [xScale, yScale],
center: {x: this.imgCanvas.position[2], y: this.imgCanvas.position[3]}
})
}
break;
case 'tc':
yScale = (height - move.y) / height
if (yScale > 0.1) {
transfrom.push({
scale: [1, yScale],
center: {x: 0, y: this.imgCanvas.position[3]}
})
}
break;
case 'tr':
xScale = (width + move.x) / width
yScale = (height - move.y) / height
if (xScale > 0.1 && yScale > 0.1) {
transfrom.push({
scale: [xScale, yScale],
center: {x: 0, y: this.imgCanvas.position[3]}
})
}
break;
case 'rc':
xScale = (width + move.x) / width
if (xScale > 0.1) {
transfrom.push({
scale: [xScale, 1],
center: {x: 0, y: this.imgCanvas.position[3]}
})
}
break;
case 'lc':
xScale = (width - move.x) / width
if (xScale > 0.1) {
transfrom.push({
scale: [xScale, 1],
center: {x: this.imgCanvas.position[2], y: this.imgCanvas.position[3]}
})
}
break;
case 'br':
xScale = (width + move.x) / width
yScale = (height + move.y) / height
if (xScale > 0.1 && yScale > 0.1) {
transfrom.push({
scale: [xScale, yScale],
center: {x: 0, y: 0}
})
}
break;
case 'bl':
xScale = (width - move.x) / width
yScale = (height + move.y) / height
if (xScale > 0.1 && yScale > 0.1) {
transfrom.push({
scale: [xScale, yScale],
center: {x: this.imgCanvas.position[2], y: 0}
})
}
break;
case 'bc':
yScale = (height + move.y) / height
if (yScale > 0.1) {
transfrom.push({
scale: [1, yScale],
center: {x: 0, y: 0}
})
}
break;
}
} else if (this.oper === 'rotate') {
let move = ev.clientX - this.startMovePos.x
let height = this.imgCanvas.position[3]
let width = this.imgCanvas.position[2]
let center = {x: width / 2, y: height / 2}
// let zrotate = transfrom.
transfrom.push({
rotate: move / 3,
center: center
})
}
this.startMovePos = {
x: ev.clientX,
y: ev.clientY
}
this.imgCanvas.imageLayer.refresh()
},
upMove() {
this.moveing = false
this.mapDown = false
this.oper = null
this.dir = null
this.startMovePos = null
},
getInfo() {
return {
pos: this.boxPos,
img: this.args.img
}
}
},
computed: {
boxStyle() {
if (this.box && Object.keys(this.box).length) {
const box = this.box
return {
width: box.width + 20 + 'px',
height: box.height + 20 + 'px',
left: box.cc.x + 'px',
top: box.cc.y + 'px'
}
} else {
return {}
}
},
boxPos() {
if (this.box && Object.keys(this.box).length) {
const ret = {}
for (let key in this.box) {
if (key !== 'width' && key !== 'height') {
ret[key] = this.map.screenToLatlan(this.box[key])
}
}
return ret
} else {
return {}
}
}
},
mounted() {
document.documentElement.addEventListener('mousemove', ev => {
ev.stopPropagation()
ev.preventDefault()
this.moveHandle.bind(this)(ev)
this.move.bind(this)(ev)
})
document.documentElement.addEventListener('mousedown', ev => {
this.mapStartHandle.bind(this)(ev)
})
document.documentElement.addEventListener('mouseup', ev => {
ev.stopPropagation()
ev.preventDefault()
this.upMove.bind(this)()
})
this.map = initMap(this.mapOl)
}
})
})();