(() => { // 初始地图 const initMap = (map) => { let cacheCanvas const ctrl = { map, async loadImage(args) { ctrl.remove() const { file, minWidth, minHeight } = args args.img = args.img ? args.img : await blobImageLoad(file, minWidth, minHeight) cacheCanvas = loadImageLayer(map, args) return cacheCanvas }, remove() { if (cacheCanvas && cacheCanvas.__layer) { map.removeLayer(cacheCanvas.__layer) } cacheCanvas = null }, 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 } } return ctrl } 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.removeLayer(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 canvas.__layer = image 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-l03EZNS' : 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 Promise.all([ axios.post(`${root}/indoor/${sceneCode}/api/update/coord`, { param: data }), axios.put(`${root}/indoor/${sceneCode}/api/tiled_maps`, { location: data.location, map_size_m: data.map_size_m, orientation: data.orientation, }), ]) 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: `