(() => {
// 初始地图
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:99999', 'EPSG:99999')
var latlan = proj4("EPSG:3857","EPSG:4490",real);
return latlan
}
}
}
function toArray(quaternion){
var rot90 = (new THREE.Quaternion).setFromAxisAngle(new THREE.Vector3(0,0,1), THREE.Math.degToRad(-90)) //add 转入时旋转90度
, rot90Invert = rot90.clone().inverse()//add 转出时旋回90度
var t1 = quaternion.clone().multiply(rot90Invert);
var e = t1.toArray();
return [e[3], e[0], e[1], e[2]]
}
function getQuaternion(angle){//angle:0-360 角度
var quaternion = new THREE.Quaternion().setFromEuler(new THREE.Euler(0,0,THREE.Math.degToRad(-angle)));
return toArray(quaternion)
}
function getSize(imgWidth, scale){//imgWidth:图片宽度, scale缩放值(x==y)
var level = imgWidth / 1024; //以1024为基准
return 95.54628610610962 * level * scale;
}
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) => {
console.log(scale)
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 canvas = document.createElement('canvas')
const loadImage = (map, args, itude) => {
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
}
})
console.log(scale, size)
// 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) => {
const analysis = (blob) => new Promise((resolve, reject) => {
const url = typeof blob !== 'string' ?
window.URL.createObjectURL(blob) :
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)
};
}
const sceneName = window.location.pathname.split('/')[2]
const isDev = !sceneName || sceneName === 'addDataSet.html'
const sceneCode = isDev ? 't-kJ2PEjZ' : window.location.pathname.split('/')[2]
const root = isDev ? `https://testlaser.4dkankan.com` : ''
// const root = 'http://192.168.0.135:9294'
const request = {
uploadFiles(files) {
const fromData = new FormData()
files.forEach(({ dir, file }) => {
fromData.append(dir, file)
})
return axios({
headers: { 'Content-Type': 'multipart/form-data' },
method: 'POST',
data: fromData,
url: `${root}/indoor/${sceneCode}/api/mapSmall/upload`
})
},
getDetail() {
return axios.post(`${root}/indoor/${sceneCode}/api/mapSmall/detail`)
},
updateCoord(data) {
return axios.post(
`${root}/indoor/${sceneCode}/api/update/coord`, { param: data }
)
},
getSceneInfo() {
return axios.get(`${root}/indoor/${sceneCode}/api/datasets`)
}
}
const analysisFiles = (files) => {
const imagesArray = []
const formatError = () => {
alert('目录不规范 请上传 z/x/y.png 格式目录,且在最底级目录放置图片文件')
}
let imagesXYZ = {}
for (let dir in files) {
let file = files[dir]
let locals = dir.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 && Array.isArray(current)) {
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
const getNameNum = (str) => {
let rg = str.match(/[\/\\]([^\/\\]*)?\.[^\/\\]*$/)
return weight = rg ? parseInt(rg[1]) : 999
}
Object.keys(imagesXYZ).sort((a, b) => a - b).forEach(key => {
imagesArray.push(
imagesXYZ[key].sort((a, b) => {
let wa = typeof a === 'string' ?
getNameNum(a) :
parseInt(a.name)
let wb = typeof b === 'string' ?
getNameNum(b) :
parseInt(b.name)
return wa - wb
})
)
})
return imagesArray
}
// 目录:
Vue.component('imageTranform', {
props: ['mapOl'],
name: 'imageTranform',
template: `
`,
data() {
return {
isHover: false,
box: {},
left: 0,
top: 0
}
},
methods: {
imageChange(e) {
const files = e.target.files;
if (files && files[0]) {
const file = files[0];
// onload 里面不能用this
let img = new Image();
img.src = window.URL.createObjectURL(file);
img.onload = async() => {
if (img.width % 256 == 0 && img.height % 256 == 0) {
let imagesArray = []
if (e.target.files.length > 1) {
const files = {}
for (let file of e.target.files) {
files[file.webkitRelativePath] = file
}
imagesArray = analysisFiles(files)
} else {
imagesArray = [
[e.target.files[0]]
]
}
if (this.imgCanvas) {
ctx = this.imgCanvas.getContext('2d')
ctx.clearRect(-10000, -10000, 10000, 10000)
this.imgCanvas.imageLayer.refresh()
}
await this.drawCanvas(imagesArray, [], {
lat: this.lat,
lon: this.lon
})
} else {
alert('图片宽高需为256的倍数')
}
};
}
},
async drawCanvas(imagesArray, transfroms, { lat, lon } = {}) {
try {
this.transfroms = transfroms || []
this.args = {
draw: (ctx) => {
this.drawIng = false
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: lon || 113.59963069739054,
lat: lat || 22.364821730960752,
translate: { x: 0, y: 0 },
scale: [1, 1],
direction: 0
}
this.imgCanvas = await this.map.loadImage(this.args)
} catch (e) {
console.error(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) {
if (!this.time && !this.drawIng) {
this.move(e)
this.time = null
}
} 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 || this.drawIng) 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.length = 0
transfrom.push({ translate: move })
} else if (this.oper === 'scale') {
const doScale = (transfrom && transfrom[0] && transfrom[0].scale) || [1, 1]
move.x = move.x * doScale[0]
move.y = move.y * doScale[1]
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 < yScale) {
yScale = xScale
} else {
xScale = yScale
}
if (xScale > 0 && yScale > 0) {
transfrom.length = 0
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) {
transfrom.length = 0
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 > yScale) {
yScale = xScale
} else {
xScale = yScale
}
if (xScale > 0 && yScale > 0) {
transfrom.length = 0
transfrom.push({
scale: [xScale, yScale],
center: { x: 0, y: this.imgCanvas.position[3] }
})
}
break;
case 'rc':
xScale = (width + move.x) / width
if (xScale > 0) {
transfrom.length = 0
transfrom.push({
scale: [xScale, 1],
center: { x: 0, y: this.imgCanvas.position[3] }
})
}
break;
case 'lc':
xScale = (width - move.x) / width
if (xScale > 0) {
transfrom.length = 0
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 < yScale) {
yScale = xScale
} else {
xScale = yScale
}
if (xScale > 0 && yScale > 0) {
transfrom.length = 0
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 < yScale) {
yScale = xScale
} else {
xScale = yScale
}
if (xScale > 0 && yScale > 0) {
transfrom.length = 0
transfrom.push({
scale: [xScale, yScale],
center: { x: this.imgCanvas.position[2], y: 0 }
})
}
break;
case 'bc':
yScale = (height + move.y) / height
if (yScale > 0) {
transfrom.length = 0
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.length = 0
transfrom.push({
rotate: move / 3,
center: center
})
}
// this.startMovePos = {
// x: ev.clientX,
// y: ev.clientY
// }
this.drawIng = true
this.imgCanvas.imageLayer.refresh()
},
upMove() {
this.moveing = false
this.mapDown = false
this.oper = null
this.dir = null
this.startMovePos = null
},
uploadData() {
if (!this.args) {
return Promise.resolve(true)
}
const promises = []
const files = []
for (let i = 0; i < this.args.img.length; i++) {
const images = this.args.img[i]
for (let j = 0; j < images.length; j++) {
const file = images[j][2]
if (typeof file !== 'string') {
const suffix = file.type.substr(file.type.indexOf('/') + 1)
files.push({ dir: `${i}/${j}.${suffix}`, file })
}
}
}
if (files.length) {
if (files.length === 1) {
const file = files[0]
files.length = 0
files.push({
...file,
dir: file.file.name
})
}
promises.push(
request.uploadFiles(files)
)
}
promises.push(
request.updateCoord({
...this.boxPos,
transfroms: this.transfroms,
})
)
return Promise.all(promises)
},
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])
}
}
let rotate = 0
let scale = { x: 1, y: 1 }
this.transfroms.forEach(items => {
items.forEach(item => {
if (item.rotate) {
rotate = Number((rotate + Number(item.rotate)).toFixed(2))
}
if (item.scale) {
scale.x *= item.scale[0]
scale.y *= item.scale[1]
}
})
})
ret.rotate = rotate
ret.scale = scale
let ctx = this.imgCanvas.getContext('2d')
let key = ['a', 'b', 'c', 'd', 'e', 'f']
let imatrix = ctx.getTransform()
ret.imatrix = {}
key.forEach(k => ret.imatrix[k] = imatrix[k])
// 缩放,坐标,角度
ret.map_size_m = getSize(this.imgCanvas.position[2], scale.x),
ret.location = ret.cc,
ret.orientation = getQuaternion(rotate)
return ret
} else {
return {}
}
}
},
mounted() {
Promise.all([
request.getDetail(),
request.getSceneInfo()
]).then(async([res1, res2]) => {
const {
path,
position
} = res1.data.data
const { location } = res2.data[0]
if (path && path.length > 0) {
const files = {}
path.forEach(path => (files[path] = root + path))
await this.drawCanvas(
analysisFiles(files),
position ? position.transfroms : [], {
lat: location[1],
lon: location[0],
}
)
}
this.lat = location[1]
this.lon = location[0]
})
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.$nextTick(() => {
this.map = initMap(this.mapOl)
})
}
})
})();