import createPop from "./createPop"; import regulatorBox from "../components/PopWindow/regulatorBox.vue"; import valveWell from "../components/PopWindow/valveWell.vue"; import flowMeter from "../components/PopWindow/flowMeter.vue"; import pipelineView from "../components/PopWindow/pipelineView.vue"; import lineInfoWindow from "../components/PopWindow/lineInfoWindow.vue"; import { delDeviceInfo } from "@/api/device/deviceInfo"; import markerInfoWindow from "../components/PopWindow/markerInfoWindow.vue"; import workerManInfowindow from "../components/PopWindow/workerManInfowindow.vue"; import { getArray } from "@/utils/gassafety.js"; import { delPipe } from "@/api/device/pipe.js"; import vue from "../main"; import {Card} from "element-ui" let defaultCenter = "石家庄"; export let map; export const DEVICE_TYPE = { PIPEPLINE: "1", REGEULATORBOX: "2", VALUEWELL: "3", FLOWMETER: "4", DUTYPERSON: "5", WORKORDER: "6", PRESSUREGAGE: "7", INSPECTOR: "8" }; class gaodeMap { // 所有线的数组 polyLines = []; // 是否开启新增 lineType = 0; // 创建一个新的lineObj newLineObj = null; // onceFlag=false; lineFlag = false; mouseTool = null; myMap = null; showInfoWindow = null; //构造函数中设置中央点默认值 constructor(center) { this.markers = []; this.center = center ? center : defaultCenter; map = new AMap.Map("container", { //mask: addMask(result.districtList[0].boundaries), center: [114.72995, 38.37417], // resizeEnable: true, disableSocket: true, viewMode: "3D", showLabel: true, // labelzIndex: 110, pitch: 8, zoom: 9 //mapStyle: 'amap://styles/darkblue', // mapStyle: 'amap://styles/3b679a15f448a4740ba2ff7524e1a4ae', }); this.myMap = map; this.myMap.on("click", e => { // if (this.lineType !=1 || this.lineFlag) return; if (this.lineType != 1 || this.lineFlag) return; // 除了这里,还要利用vue页面的window事件辅助,当组件出来的时候,就得利用window事件 if (this.newLineObj) { vue .$confirm("是否重新画管道") .then(() => { this.newLineObj.polyEditor.close(); map.remove(this.newLineObj); this.newLineObj = null; this.lineFlag = false; this.createNewLine(); //console.log("map点击事件新建"); }) .catch(() => {}); } // //console.log("抬起来了"); }); this.districtBoundaries(); } /** * 获取map * @returns {AMap.Map} */ get getMap() { return this.map; } /** * 重新设置中央点 * @param value */ set setCenter(value) { this.center = value; this.districtBoundaries(); } resetMapCenter(value) { map.setCenter(value); } /** * 设置城市边界 */ districtBoundaries() { let that = this; let opts = { subdistrict: 0, extensions: "all", level: "city" }; let district = new AMap.DistrictSearch(opts); district.search(that.center, function(status, result) { if (status == "complete") { let defaultCenter = []; defaultCenter.push(result.districtList[0].center.lng); defaultCenter.push(result.districtList[0].center.lat); map.setCenter(defaultCenter); let bounds = result.districtList[0].boundaries; for (let i = 0; i < bounds.length; i += 1) { new AMap.Polyline({ path: bounds[i], strokeColor: "#0088ff", strokeWeight: 5, strokeOpacity: 7, map: map }); } } }); } /** * 添加Market * @param markerType * @param lnglats */ addMarker(markerType, data) { let that = this; that.markerType = markerType; let infoWindow = new AMap.InfoWindow({ isCustom: true, offset: new AMap.Pixel(24, -38), anchor: "left-top" }); infoWindow.on("mouseover", function() { let options = map.getStatus(); options.scrollWheel = false; map.setStatus(options); }); infoWindow.on("mouseout", function() { let options = map.getStatus(); options.scrollWheel = true; map.setStatus(options); }); let marker = new AMap.Marker({ position: [data.longitude, data.latitude], map: map, offset: new AMap.Pixel(0, 5) }); this.setMarkerIcon(marker); if (DEVICE_TYPE.INSPECTOR == markerType) { // 存值 const { createTime,locationId,longitude,latitude} = data; marker.setExtData({createTime, locationId ,pos:[longitude,latitude]}); // 值班人员的事件 marker.on("click", e => { console.log(e); console.log(e.target.getExtData()) // console.log(Card) // e.target.content = this.getMarketContent(data); }); marker.on("mouseover",(e)=>{ e.target.content = this.getMarketContent(data); infoWindow.setContent(e.target.content); infoWindow.open(map, e.target.getPosition()); e.target.cloneDom = infoWindow.dom.cloneNode(true); // console.log(infoWindow.dom.offsetLeft); // console.log(infoWindow.dom); e.target.cloneDom.style.top = infoWindow.dom.offsetTop + 80 + "px"; e.target.cloneDom.style.left = infoWindow.dom.offsetLeft + 100 + "px"; // 先删除之前的,后增加现在的 // this.removeCloneDom(); that.removeCloneDom(); document.body.appendChild(e.target.cloneDom); that.workerManCloneDom = e.target.cloneDom; infoWindow.close(); that.workerManInfoWindow = infoWindow; }) } if ( DEVICE_TYPE.WORKORDER != markerType && DEVICE_TYPE.INSPECTOR != markerType ) { marker.content = this.getMarketContent(data); marker.on("mouseover", infoOpen); marker.on("mouseout", infoClose); marker.setExtData(data); that.deviceType = markerType; let editWindow = that.createInfowindow("编辑"); editWindow.fileArr = data.iconUrl != "" && data.iconUrl != undefined ? [{ url: data.iconUrl }] : []; editWindow.obj = marker; editWindow.gaoMap = that; editWindow.map = map; marker.on("click", function(e) { if ("edit" == that.mapOperateType) { editWindow.form = e.target.getExtData(); editWindow.show(); } else if ("delete" == that.mapOperateType) { let diviceType = { "2": "调压箱", "3": "阀门井", "4": "流量计", "7": "压力表" }; vue .$confirm( '是否删除名称为"' + e.target.getExtData().deviceName + '"的' + diviceType[markerType] + " ?", "提示", { type: "warning" // center: true } ) .then(() => { map.remove(marker); that.deleteMarker(markerType, data.deviceId); }) .catch(() => { vue.$message({ type: "info", // center:true, offset: 100, message: "已取消删除" }); }); } /*else { //map.setZoomAndCenter(13, e.target.getPosition()); let infoWindow = new AMap.InfoWindow({ isCustom: true, offset: new AMap.Pixel(24, -38), anchor: "left-top" }); infoWindow.setContent(e.target.content); infoWindow.open(map, e.target.getPosition()); }*/ }); marker.on("dragend", function(e) { editWindow.form.longitude = e.lnglat.lng; editWindow.form.latitude = e.lnglat.lat; }); } this.markers.push(marker); map.setZoom("11"); //map.setFitView(); function infoClose(e) { let options = map.getStatus(); options.scrollWheel = true; map.setStatus(options); //infoWindow.close(map, e.target.getPosition()); } function infoOpen(e) { // 删除克隆出来的线的clonedom let options = map.getStatus(); options.scrollWheel = false; map.setStatus(options); infoWindow.setContent(e.target.content); infoWindow.open(map, e.target.getPosition()); // console.log(infoWindow.dom); // 这个是为了盖住vue里的东西 e.target.cloneDom = infoWindow.dom.cloneNode(true); // console.log(infoWindow.dom.offsetLeft); // console.log(infoWindow.dom); e.target.cloneDom.style.top = infoWindow.dom.offsetTop + 80 + "px"; e.target.cloneDom.style.left = infoWindow.dom.offsetLeft + 100 + "px"; // 先删除之前的,后增加现在的 // this.removeCloneDom(); that.removeCloneDom(); document.body.appendChild(e.target.cloneDom); that.markerCloneDom = e.target.cloneDom; infoWindow.close(); that.markerInfoWindow = infoWindow; } } /** * marker添加拖拽事件 */ addMarkerDragg() { for (var i = 0; i < this.markers.length; i++) { this.markers[i].setDraggable(true); } } /** * marker移除拖拽事件 */ removeMarkerDragg() { for (var i = 0; i < this.markers.length; i++) { this.markers[i].setDraggable(false); } } /** * 添加marker信息 * @param data * @returns {string} */ getMarketContent(data) { switch (this.markerType) { case DEVICE_TYPE.REGEULATORBOX: { const dom = createPop(markerInfoWindow, { title: "调压箱", data: data, map: map }); const html = dom.$el; dom.remove(); return html; } case DEVICE_TYPE.VALUEWELL: { const dom = createPop(markerInfoWindow, { title: "阀门井", data: data, map: map }); const html = dom.$el; dom.remove(); return html; } case DEVICE_TYPE.FLOWMETER: { const dom = createPop(markerInfoWindow, { title: "流量计", data: data, map: map }); const html = dom.$el; dom.remove(); return html; } case DEVICE_TYPE.PRESSUREGAGE: { const dom = createPop(markerInfoWindow, { title: "压力表", data: data, map: map }); const html = dom.$el; dom.remove(); return html; } case DEVICE_TYPE.INSPECTOR: { const dom = createPop(workerManInfowindow, { title: "值班人员", data: data, map: map }); const html = dom.$el; dom.remove(); return html; } } } /** * 删除marker * @param delType * @param deviceId */ deleteMarker(delType, deviceId) { if ( DEVICE_TYPE.REGEULATORBOX == delType || DEVICE_TYPE.VALUEWELL == delType || DEVICE_TYPE.FLOWMETER == delType || DEVICE_TYPE.PRESSUREGAGE == delType ) { delDeviceInfo(deviceId); } } /** * 设置设备图标 * @param marker */ setMarkerIcon(marker) { switch (this.markerType) { case DEVICE_TYPE.REGEULATORBOX: { let icon = new AMap.Icon({ //size: new AMap.Size(51, 23), image: require("../assets/images/tiaoyaxiang.png") }); marker.setIcon(icon); break; } case DEVICE_TYPE.VALUEWELL: { let icon = new AMap.Icon({ //size: new AMap.Size(51, 23), image: require("../assets/images/famenjing.png") }); marker.setIcon(icon); break; } case DEVICE_TYPE.FLOWMETER: { let icon = new AMap.Icon({ //size: new AMap.Size(51, 23), image: require("../assets/images/liuliangji.png") }); marker.setIcon(icon); break; } case DEVICE_TYPE.DUTYPERSON: { let icon = new AMap.Icon({ //size: new AMap.Size(51, 23), image: require("../assets/images/zhibanrenyuan.png") }); marker.setIcon(icon); break; } case DEVICE_TYPE.WORKORDER: { let icon = new AMap.Icon({ //size: new AMap.Size(51, 23), image: require("../assets/images/zhibanrenyuan.png") }); marker.setIcon(icon); break; } case DEVICE_TYPE.PRESSUREGAGE: { let icon = new AMap.Icon({ //size: new AMap.Size(51, 23), image: require("../assets/images/yalibiao.png") }); marker.setIcon(icon); break; } case DEVICE_TYPE.INSPECTOR: { let icon = new AMap.Icon({ //size: new AMap.Size(51, 23), image: require("../assets/images/zhibanrenyuan.jpg") }); marker.setIcon(icon); break; } } } /** * 添加折线 * @param path */ addPolyline(arr) { // this` polyLines = []; //console.log("包装的数组", arr); for (let i = 0; i < arr.length; i++) { const item = arr[i]; let { coordinates } = item; // //console.log("coordinates",coordinates) // let path = coordinates ? getArray(coordinates) :[]; // 字符串转二维数组 let path = coordinates ? eval(coordinates) : []; let polyline = new AMap.Polyline({ path, strokeColor: "#F7FE38", strokeWeight: 4, strokeOpacity: 0.9, zIndex: 50, bubble: true, geodesic: true, extData: { type: "line", //当前line状态 0:正常状态 1:正在编辑状态 isState: 0, lineData: item } }); this.polyLines.push(polyline); // 信息窗体 const dom = createPop(lineInfoWindow, { obj: { a: 123, editorPage: true, ...item, polyline } }); // console.log("domdomdomdomdom",dom) //console.log("dom", dom.$el); dom.$el.addEventListener("mouseover", () => { let options = this.myMap.getStatus(); options.scrollWheel = false; this.myMap.setStatus(options); }); dom.$el.addEventListener("mouseout", () => { let options = this.myMap.getStatus(); options.scrollWheel = true; this.myMap.setStatus(options); }); let infoWindow = new AMap.InfoWindow({ isCustom: true, autoMove: false, content: dom.$el, //信息船体偏移量 offset: new AMap.Pixel(0, 0), anchor: "left-top" }); this.newLineAddEvent(polyline); polyline.infoWindow = infoWindow; //添加事件 polyline.on("mouseover", e => { if (this.lineType == 1) return; // 上方导航的高 const topBar = 81; // 坐标导航的宽 const leftBar = 100; // 屏幕可视区的宽高 const { clientWidth: windowClientWidth, clientHeight: windowClientHeight } = document.body; // 弹出的信息窗口的宽高 const { offsetWidth: infoWindowWidth, offsetHeight: infoWindowHeight } = { offsetWidth: 406, offsetHeight: 316 }; // 鼠标碰到线后的位置 const { clientX: mouseClientX, clientY: mouseClientY } = e.originEvent; // 鼠标到左边界的距离 const offsetLeftX = mouseClientX - 100; // 鼠标到右边界的距离 const offsetRightX = windowClientWidth - mouseClientX; const offsetTopY = mouseClientY - 81; const offsetBottomY = windowClientHeight - mouseClientY; const offsetY = mouseClientY - 80 - infoWindowHeight; let X = 20, Y = -20; if (offsetLeftX <= infoWindowWidth) { //console.log("靠左了"); X = 20; } else if (offsetRightX <= infoWindowWidth) { //console.log("靠右了"); X = -infoWindowWidth - 20; } if (offsetTopY <= infoWindowHeight) { //console.log("靠上了"); Y = 20; } else if (offsetBottomY <= infoWindowHeight + 81) { //console.log("靠下了"); Y = -infoWindowHeight - 20; } polyline.setOptions({ strokeColor: "#FF5A67" }); infoWindow.setOffset(new AMap.Pixel(X, Y)); infoWindow.open(map, e.lnglat); // 这个是为了盖住vue里的东西 polyline.cloneDom = infoWindow.dom.cloneNode(true); // console.log(infoWindow.dom.offsetLeft); // console.log(infoWindow.dom); polyline.cloneDom.style.top = infoWindow.dom.offsetTop + 80 + "px"; polyline.cloneDom.style.left = infoWindow.dom.offsetLeft + 100 + "px"; // 先删除之前的,后增加现在的 this.removeCloneDom(); document.body.appendChild(polyline.cloneDom); infoWindow.close(); // polyline.infoWindow=infoWindow; this.showInfoWindow = infoWindow; this.cloneDom = polyline.cloneDom; // const }); polyline.on("mouseout", e => { polyline.setOptions({ strokeColor: "#F7FE38" }); // infoWindow.close(); }); // 计算info的位置 // function infoPosition() {} } map.add(this.polyLines); // 缩放地图到合适的视野级别 // map.setFitView(); } removeCloneDom() { // 线 this.cloneDom && document.body.removeChild(this.cloneDom); // 设备 this.markerCloneDom && document.body.removeChild(this.markerCloneDom); // 值班人员 this.workerManCloneDom && document.body.removeChild(this.workerManCloneDom); // that.workerManInfoWindow = infoWindow; this.cloneDom = null; this.markerCloneDom = null; this.workerManCloneDom = null; } // 创建一条新的线 createNewLine() { map.remove(this.markerOverlays); this.mouseTool.polyline({ // bubbles:false, strokeWeight: 4, strokeColor: "#80d8ff", extData: { type: "newLine", //当前line状态 0:正常状态 1:正在编辑状态 isState: 0 } //同Polyline的Option设置 }); // 让它不再等于null // this.newLineObj={a:123} } // 创建出来的新线归位,重置,当点击编辑跟删除的时候 newLineReset() { this.lineFlag = false; if (this.mouserTool) { this.mouseTool.close(); } if (this.newLineObj) { this.newLineObj.polyEditor.close(); map.remove(this.newLineObj); this.newLineObj = null; } } // 新建line增加编辑以及右键菜单 closeInfoWindow() { this.showInfoWindow && this.showInfoWindow.close(); // 删除浮动到线上的cloneDom this.removeCloneDom(); this.markerInfoWindow && this.markerInfoWindow.close(); } // 新line与老line添加点击事件 newLineAddEvent(obj) { obj.polyEditor = new AMap.PolyEditor(map, obj); // obj.polyEditor.on('adjust',(e)=>{ // //console.log("addnode") // }) obj.on("mousedown", e => { //console.log(e.originEvent); //console.log("lineType", this.lineType); this.lineFlag = true; // 每次点击关闭然后再次激活 // 如果是新线就关闭 if (obj.getExtData().type == "newLine") { this.mouseTool.close(); } // 删除 if (this.lineType == 3) { this.lineDelete(obj); return; } // 获取当前状态 0普通状态,1是正编辑状态 const { isState, type } = obj.getExtData(); // 如果不是新线的时候并且没点编辑,点击是无效的 if (type != "newLine" && this.lineType != 2) return; if (isState == 0) { // 如果是0,就打开编辑,变成编辑状态 obj.polyEditor.open(); let opstions = obj.getExtData(); opstions.isState = 1; //console.log(opstions); obj.setExtData(opstions); } else { // 经纬度 const lnglatsArr = obj.getPath().map(item => [item.lng, item.lat]); // 管道总长度 const pipeLength = obj.getLength(); // 传回来的数据 如果是新管道就是空 const lineData = type == "newLine" ? {} : obj.getExtData().lineData; // const lineData = obj.getExtData().lineData; //console.log("lineData===================>传入组件的数据", lineData); this.infoWindowPipelineView({ target: obj, lineType: type, lnglatsArr, pipeLength, lineData }); } }); } // 隐藏所有管道 lineShow(bool) { bool ? this.polyLines.forEach(item => item.show()) : this.polyLines.forEach(item => item.hide()); if (this.newLineObj) { bool ? this.newLineObj.show() : this.newLineObj.hide(); } } markerShow(type, bool) { this.markers.forEach(item => { const { deviceType } = item.getExtData(); if (deviceType == type) { bool ? item.show() : item.hide(); } // //console.log("deviceType",deviceType); }); } lineDelete(obj) { const { lineData: { pipeName } } = obj.getExtData(); vue .$confirm(`是否删除名称为"${pipeName}"的管道 ?`, "提示", { // confirmButtonText: "确定", // cancelButtonText: "取消", type: "warning" // center: true }) .then(() => { const { lineData: { pipeId } } = obj.getExtData(); //console.log(pipeId); delPipe(pipeId).then(res => { const index = this.polyLines.indexOf(obj); // 如果是老线,就要线删除原来的,然后重新包装一遍 if (index >= 0) { this.polyLines.splice(index, 1); map.remove(obj); } vue.$message({ type: "success", offset: 100, // center:true, message: "删除成功!" }); // 关闭当前线条的infowindow // this.closeLineInfoWindow(); }); }) .catch(() => { vue.$message({ type: "info", // center:true, offset: 100, message: "已取消删除" }); }); } // 关闭所有已经上传的线的编辑状态 linePolyEditorAllClose() { this.polyLines.forEach(item => { item.polyEditor.close(); let opstions = item.getExtData(); opstions.isState = 0; item.setExtData(opstions); let attr = item.getOptions(); attr.strokeColor = "#F7FE38"; item.setOptions(attr); }); } // 传进组件的会调 点确认的时候调 lineOkCallBack(target, data) { target.polyEditor.close(); const index = this.polyLines.indexOf(target); // 如果是老线,就要线删除原来的,然后重新包装一遍 if (index >= 0) { this.polyLines.splice(index, 1); } //console.log("data999-=================>传回来的data", data); this.addPolyline([data]); map.remove(target); // //console.log('polyLines',this.polyLines) } // 上传服务器用的组件 infoWindowPipelineView(options) { // const {} =options; const notice = createPop(pipelineView, { title: "管道", // 数据 // lineData: options.lineData, // lnglatsArr:options.lnglatsArr, // pipeLength:options.pipeLength, // //线是新线还是老线 // lineType: options.lineType, // target: options.obj, ...options, //把当前对象当作that传进去 gaodeMap: this, lineOkCallBack: this.lineOkCallBack }); notice.show(); } /** * 添加鼠标事件 */ addMouseTool() { this.mouseTool = new AMap.MouseTool(map); //监听draw事件可获取画好的覆盖物 this.overlays = []; let that = this; this.markerOverlays = []; this.mouseTool.on("draw", function(e) { if (e.obj.getExtData().type != "newLine") { map.remove(that.markerOverlays); const device = that.createInfowindow("新增"); device.map = map; device.obj = e.obj; device.gaoMap = that; e.obj.on("click", function(aa) { let postion = aa.target._position; //兼容拖拽后单击事件,拖拽后点击事件返回位置为数组 if (postion instanceof Array) { device.form.longitude = postion[0]; device.form.latitude = postion[1]; } else { device.form.longitude = postion.lng; device.form.latitude = postion.lat; } device.show(); }); e.obj.on("mouseover", function() { that.mouseTool.close(); }); e.obj.on("mouseout", function() { that.draw(that.deviceType); }); that.markerOverlays.push(e.obj); } if (e.obj && e.obj.getExtData().type == "newLine") { //console.log("挂上事件"); that.newLineAddEvent(e.obj); // 记录这条线 // //console.log(e.obj.getPath().length) // 如果只有一个点,并没有连成线的时候就不close 大于一个点的时候才执行clse //console.log(that.lineType) if (e.obj.getPath().length > 1) { that.newLineObj = e.obj; that.mouseTool.close(); } else { // that.newLineObj = null; } // 关闭 // 创建一条新线,然后在点地图的时候删除原来的旧线 // that.createNewLine(); // 右键菜单 // that.addEditorAndMenu(e.obj) } // if(e.obj.getExtData().type=="line"){ // return; // } //that.mouseTool.close(); that.overlays.push(e.obj); }); } /** * 创建弹框事件 */ createInfowindow(val) { switch (this.deviceType) { case DEVICE_TYPE.PIPEPLINE: { break; } case DEVICE_TYPE.REGEULATORBOX: { return createPop(regulatorBox, { title: val + "调压箱" }); } case DEVICE_TYPE.VALUEWELL: { return createPop(valveWell, { title: val + "阀门井" }); } case DEVICE_TYPE.FLOWMETER: { return createPop(flowMeter, { title: val + "流量计" }); } case DEVICE_TYPE.PRESSUREGAGE: { return createPop(flowMeter, { title: val + "压力表" }); } } } /** * 鼠标画图事件 * @param deviceType 设备类型 */ draw(deviceType) { let that = this; that.deviceType = deviceType; if ( DEVICE_TYPE.REGEULATORBOX == deviceType || DEVICE_TYPE.VALUEWELL == deviceType || DEVICE_TYPE.FLOWMETER == deviceType || DEVICE_TYPE.PRESSUREGAGE == deviceType ) { that.mouseTool.marker({ draggable: true }); } if (DEVICE_TYPE.PIPEPLINE == deviceType) { that.mouseTool.polyline({ strokeWeight: 9, strokeColor: "#80d8ff" //同Polyline的Option设置 }); } } /** * 添加地图控件 */ addMapControl() { AMap.plugin( [ "AMap.ToolBar", "AMap.Scale", "AMap.HawkEye", "AMap.MapType", "AMap.Geolocation" ], function() { // 在图面添加工具条控件,工具条控件集成了缩放、平移、定位等功能按钮在内的组合控件 //map.addControl(new AMap.ToolBar()); // 在图面添加比例尺控件,展示地图在当前层级和纬度下的比例尺 map.addControl(new AMap.Scale()); // 在图面添加鹰眼控件,在地图右下角显示地图的缩略图 map.addControl(new AMap.HawkEye({ isOpen: true })); // 在图面添加类别切换控件,实现默认图层与卫星图、实施交通图层之间切换的控制 map.addControl( new AMap.MapType({ position: { top: "10px", left: "100px" } }) ); // 在图面添加定位控件,用来获取和展示用户主机所在的经纬度位置 // map.addControl(new AMap.Geolocation()); } ); } searchTips(inputId) { let that = this; AMap.plugin(["AMap.AutoComplete", "AMap.PlaceSearch"], function() { //输入提示 // var autoOptions = { // input: inputId // }; // let auto = new AMap.AutoComplete(autoOptions); that.placeSearch = new AMap.PlaceSearch({ map: map }); //构造地点查询类 // auto.on("select", function(e) { // that.searchSelectAdcode = e.poi.adcode; // that.searchSelectName = e.poi.name; // }); }); } closeAddMarker() { this.mouseTool.close(); map.remove(this.markerOverlays); } } export default gaodeMap;