(() => { // 初始地图 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: `
单文件:
{{key}} {{item}}
`, 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) }) } }) })();