|
|
|
<template>
|
|
|
|
<div id="map">
|
|
|
|
<div class="search">
|
|
|
|
<el-input placeholder="请输入内容"
|
|
|
|
v-model="input"
|
|
|
|
clearable
|
|
|
|
size="medium">
|
|
|
|
<template slot="append">
|
|
|
|
<span style="cursor: pointer;"
|
|
|
|
@click="goSearch">搜索</span>
|
|
|
|
</template>
|
|
|
|
</el-input>
|
|
|
|
</div>
|
|
|
|
<div id="mapContent"
|
|
|
|
style="width: 100%;height: 100%;">
|
|
|
|
<div id="popup">
|
|
|
|
<a href="#"
|
|
|
|
id="popup-closer"></a>
|
|
|
|
<div id="popup-title">
|
|
|
|
场所信息
|
|
|
|
</div>
|
|
|
|
<div>
|
|
|
|
<div>
|
|
|
|
<span>场所名称:{{featureInfo.place_name||"--"}}</span>
|
|
|
|
</div>
|
|
|
|
<div style="margin: 12px 0;">
|
|
|
|
<span>场所类型:{{featureInfo.place_type||"--"}}</span>
|
|
|
|
</div>
|
|
|
|
<div>
|
|
|
|
<span>场所地址:{{featureInfo.place_address||"--"}}</span>
|
|
|
|
</div>
|
|
|
|
<div class="moreInfo"
|
|
|
|
@click="ifShowDetails=true">更多信息</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<el-dialog :visible.sync="ifShowDetails"
|
|
|
|
width="60%"
|
|
|
|
:modal="false">
|
|
|
|
<span slot="title"
|
|
|
|
style="font-size:20px;font-weight:700">
|
|
|
|
{{ featureInfo.place_name }}的更多信息
|
|
|
|
</span>
|
|
|
|
<Dialog :info="featureInfo"></Dialog>
|
|
|
|
<div slot="footer"
|
|
|
|
style="display: flex;justify-content: space-evenly;">
|
|
|
|
<el-button @click="ifShowDetails = false">取 消</el-button>
|
|
|
|
<el-button type="primary"
|
|
|
|
@click="ifShowDetails = false">确 定</el-button>
|
|
|
|
</div>
|
|
|
|
</el-dialog>
|
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
<script>
|
|
|
|
|
|
|
|
import dy from '@/assets/images/fifteen/dy.png';
|
|
|
|
import education from '@/assets/images/fifteen/education.png';
|
|
|
|
import jr from '@/assets/images/fifteen/jr.png';
|
|
|
|
import qt from '@/assets/images/fifteen/qt.png';
|
|
|
|
import sy from '@/assets/images/fifteen/sy.png';
|
|
|
|
import sz from '@/assets/images/fifteen/sz.png';
|
|
|
|
import wt from '@/assets/images/fifteen/wt.png';
|
|
|
|
import xz from '@/assets/images/fifteen/xz.png';
|
|
|
|
import yl from '@/assets/images/fifteen/yl.png';
|
|
|
|
import { requestPostBi } from "@/js/dai/request-bipass";
|
|
|
|
import { Feature, Overlay } from 'ol';
|
|
|
|
import Map from 'ol/Map';
|
|
|
|
import View from 'ol/View';
|
|
|
|
import { defaults as defaultControls } from 'ol/control.js';
|
|
|
|
import { Circle, Point, Polygon } from 'ol/geom';
|
|
|
|
import { Tile as TileLayer, Vector as VectorLayer } from 'ol/layer';
|
|
|
|
import { getPointResolution } from 'ol/proj';
|
|
|
|
import { METERS_PER_UNIT } from 'ol/proj/Units';
|
|
|
|
import { Vector as VectorSource, XYZ } from 'ol/source';
|
|
|
|
import { Fill, Icon, Stroke, Style, Text } from 'ol/style';
|
|
|
|
import Dialog from "./dialog.vue";
|
|
|
|
export default {
|
|
|
|
components: {
|
|
|
|
Dialog
|
|
|
|
},
|
|
|
|
data() {
|
|
|
|
return {
|
|
|
|
map: null,
|
|
|
|
vectorSource: null,
|
|
|
|
vectorLayer: null,
|
|
|
|
rangeSource: null,
|
|
|
|
rangeLayer: null,
|
|
|
|
positionSource: null,
|
|
|
|
positionLayer: null,
|
|
|
|
circleSource: null,
|
|
|
|
circleLayer: null,
|
|
|
|
featureInfo: {},
|
|
|
|
overlay: null,
|
|
|
|
input: "",
|
|
|
|
ifShowDetails: false,
|
|
|
|
dialogTitle: ""
|
|
|
|
}
|
|
|
|
},
|
|
|
|
mounted() {
|
|
|
|
this.init();
|
|
|
|
|
|
|
|
},
|
|
|
|
methods: {
|
|
|
|
init() {
|
|
|
|
//高德地图也是一种瓦片图层,是通过行列号与层级(zoom)关系加载的
|
|
|
|
let gaodeMapLayer = new TileLayer({
|
|
|
|
title: "高德地图",
|
|
|
|
source: new XYZ({
|
|
|
|
url: 'http://wprd0{1-4}.is.autonavi.com/appmaptile?x={x}&y={y}&z={z}&lang=zh_cn&size=1&scl=1&style=7',
|
|
|
|
wrapX: true
|
|
|
|
})
|
|
|
|
});
|
|
|
|
|
|
|
|
this.map = new Map({
|
|
|
|
layers: [gaodeMapLayer],
|
|
|
|
view: new View({
|
|
|
|
center: [120.38140448734, 36.09344959486],
|
|
|
|
projection: 'EPSG:4326',
|
|
|
|
zoom: 14,
|
|
|
|
maxZoom: 18
|
|
|
|
}),
|
|
|
|
controls: defaultControls({
|
|
|
|
zoom: false,//禁用缩放控件
|
|
|
|
rotate: false,
|
|
|
|
}),
|
|
|
|
target: 'mapContent'
|
|
|
|
});
|
|
|
|
|
|
|
|
this.getRange()
|
|
|
|
|
|
|
|
let container = document.getElementById('popup');
|
|
|
|
//构建一个覆盖标注
|
|
|
|
this.overlay = new Overlay({
|
|
|
|
element: container,//将最外面的div容器放在覆盖标注中
|
|
|
|
autoPan: {
|
|
|
|
animation: {
|
|
|
|
duration: 250,//拖动动画的效果
|
|
|
|
},
|
|
|
|
},
|
|
|
|
});
|
|
|
|
//添加标注
|
|
|
|
this.map.addOverlay(this.overlay);
|
|
|
|
const _this = this;
|
|
|
|
// let select = new Select({
|
|
|
|
// style: new Style({
|
|
|
|
// stroke: new Stroke({
|
|
|
|
// color: "rgb(0, 153, 255,1)",
|
|
|
|
// width: 2
|
|
|
|
// }),
|
|
|
|
// fill: new Fill({
|
|
|
|
// color: "rgb(209, 203, 189,0.6)"
|
|
|
|
// })
|
|
|
|
// })
|
|
|
|
// });
|
|
|
|
// //map加载该控件,默认是激活可用的
|
|
|
|
// _this.map.addInteraction(select);
|
|
|
|
// console.log(99999, select);
|
|
|
|
// select.on("select", function (e) {
|
|
|
|
// console.log("选中要素");
|
|
|
|
// // console.log(e.selected[0].get('name')); //打印已选择的Feature的name属性
|
|
|
|
// let currentRome = e.selected[0]; //获取当前选中的节点
|
|
|
|
// console.log(e)
|
|
|
|
|
|
|
|
// })
|
|
|
|
// return
|
|
|
|
_this.map.on("click", function (e) {
|
|
|
|
_this.featureInfo = {}
|
|
|
|
let feature = _this.map.forEachFeatureAtPixel(e.pixel, function (feature) {
|
|
|
|
return feature
|
|
|
|
});
|
|
|
|
if (feature && Object.keys(feature.values_).length > 1) {
|
|
|
|
container.style.display = "inline-block"
|
|
|
|
_this.featureInfo = feature.values_ || {}
|
|
|
|
if (_this.featureInfo && Object.keys(_this.featureInfo).length > 0) {
|
|
|
|
_this.initPop(e)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
container.style.display = "none"
|
|
|
|
}
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
},
|
|
|
|
initPop(e) {
|
|
|
|
let coordinate = e.coordinate;//单击事件的坐标
|
|
|
|
this.overlay.setPosition(coordinate);//设置覆盖标注位置
|
|
|
|
//将视口的中心位置设置为鼠标点击的位置
|
|
|
|
// this.map.getView().setCenter(coordinate);
|
|
|
|
},
|
|
|
|
goMarker(data) {
|
|
|
|
document.getElementById('popup').style.display = "none"
|
|
|
|
if (this.positionLayer) {
|
|
|
|
this.map.removeLayer(this.positionLayer)
|
|
|
|
this.positionLayer = null
|
|
|
|
}
|
|
|
|
if (Array.isArray(data) && data.length > 1) {
|
|
|
|
if (this.vectorLayer) {
|
|
|
|
this.map.removeLayer(this.vectorLayer)
|
|
|
|
this.vectorLayer = null
|
|
|
|
}
|
|
|
|
//矢量标注的数据源
|
|
|
|
this.vectorSource = new VectorSource();
|
|
|
|
//矢量标注图层
|
|
|
|
this.vectorLayer = new VectorLayer({
|
|
|
|
source: this.vectorSource
|
|
|
|
});
|
|
|
|
this.map.addLayer(this.vectorLayer);
|
|
|
|
data.forEach(f => {
|
|
|
|
if (f.longitude && f.latitude) {
|
|
|
|
this.addImageVectorLabel(f)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
if (Array.isArray(data) && data.length == 1) {
|
|
|
|
//矢量标注的数据源
|
|
|
|
this.positionSource = new VectorSource();
|
|
|
|
//矢量标注图层
|
|
|
|
this.positionLayer = new VectorLayer({
|
|
|
|
source: this.positionSource
|
|
|
|
});
|
|
|
|
this.map.addLayer(this.positionLayer);
|
|
|
|
this.addImageVectorLabel(data[0], "listClick")
|
|
|
|
if (data[0]['longitude'] && data[0]['latitude']) {
|
|
|
|
this.map.getView().setCenter([data[0]['longitude'], data[0]['latitude']]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
//添加图片标注
|
|
|
|
addImageVectorLabel(data, type) {
|
|
|
|
//新建一个点要素 ol.Feature
|
|
|
|
let newFeature = new Feature({
|
|
|
|
//几何信息
|
|
|
|
geometry: new Point([Number(data.longitude), Number(data.latitude)]),
|
|
|
|
});
|
|
|
|
newFeature.setProperties(data);
|
|
|
|
//设置要素的样式
|
|
|
|
data.place_type_code
|
|
|
|
if (type == "listClick") {
|
|
|
|
data.place_type_code = "listClick"
|
|
|
|
newFeature.setStyle(this.createImageLabelStyle(data.place_type_code));
|
|
|
|
//将新要素添加到数据源中
|
|
|
|
this.positionSource.addFeature(newFeature);
|
|
|
|
} else {
|
|
|
|
newFeature.setStyle(this.createImageLabelStyle(data.place_type_code));
|
|
|
|
//将新要素添加到数据源中
|
|
|
|
this.vectorSource.addFeature(newFeature);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
createImageLabelStyle(type) {
|
|
|
|
let url
|
|
|
|
switch (type) {
|
|
|
|
case "commerce_service":
|
|
|
|
url = sy
|
|
|
|
break;
|
|
|
|
case "edu":
|
|
|
|
url = education
|
|
|
|
break;
|
|
|
|
case "finance_post":
|
|
|
|
url = jr
|
|
|
|
break;
|
|
|
|
case "medical":
|
|
|
|
url = yl
|
|
|
|
break;
|
|
|
|
case "administration":
|
|
|
|
url = xz
|
|
|
|
break;
|
|
|
|
case "culture_sport":
|
|
|
|
url = wt
|
|
|
|
break;
|
|
|
|
case "municipal":
|
|
|
|
url = sz
|
|
|
|
break;
|
|
|
|
case "others":
|
|
|
|
url = qt
|
|
|
|
break;
|
|
|
|
case "listClick":
|
|
|
|
url = dy
|
|
|
|
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return new Style({
|
|
|
|
image: new Icon(//返回一个图片标注
|
|
|
|
({
|
|
|
|
// anchor: [0.5, 60],
|
|
|
|
// anchorOrigin: 'top-right',
|
|
|
|
// anchorXUnits: 'fraction',
|
|
|
|
// anchorYUnits: 'pixels',
|
|
|
|
// offsetOrigin: 'top-right',
|
|
|
|
// offset:[0,10],
|
|
|
|
//图标缩放比例
|
|
|
|
scale: 1,
|
|
|
|
//透明度
|
|
|
|
opacity: 1,
|
|
|
|
//图标的url
|
|
|
|
src: url
|
|
|
|
})
|
|
|
|
)
|
|
|
|
});
|
|
|
|
},
|
|
|
|
async getRange() {
|
|
|
|
const url = "regional_scope";
|
|
|
|
const param = {
|
|
|
|
"org_id": localStorage.getItem("agencyId") || "",
|
|
|
|
}
|
|
|
|
const { data, code, msg } = await requestPostBi(
|
|
|
|
url,
|
|
|
|
{
|
|
|
|
queryParam: param
|
|
|
|
}
|
|
|
|
);
|
|
|
|
if (code == 0) {
|
|
|
|
data.forEach((f, i) => {
|
|
|
|
this.addPolygon(f, i)
|
|
|
|
|
|
|
|
})
|
|
|
|
}
|
|
|
|
},
|
|
|
|
addPolygon(value, index) {
|
|
|
|
if (this.rangeLayer) {
|
|
|
|
this.map.removeLayer(this.rangeLayer)
|
|
|
|
}
|
|
|
|
//矢量标注的数据源
|
|
|
|
this.rangeSource = new VectorSource();
|
|
|
|
//矢量标注图层
|
|
|
|
this.rangeLayer = new VectorLayer({
|
|
|
|
source: this.rangeSource
|
|
|
|
});
|
|
|
|
let arr = value.community_coordinates.split(",") || []
|
|
|
|
let res = [];
|
|
|
|
for (let index = 0; index < arr.length; index += 2) {
|
|
|
|
res.push([arr[index], arr[index + 1]]);
|
|
|
|
}
|
|
|
|
let polygon = new Feature({
|
|
|
|
geometry: new Polygon([res]),
|
|
|
|
});
|
|
|
|
|
|
|
|
let pointsCenter = this.getPointsCenter(res)
|
|
|
|
let point = new Feature({
|
|
|
|
geometry: new Point(pointsCenter),
|
|
|
|
});
|
|
|
|
if (index == 0) {
|
|
|
|
this.map.getView().setCenter(pointsCenter);
|
|
|
|
|
|
|
|
}
|
|
|
|
//设置区样式信息
|
|
|
|
polygon.setStyle(
|
|
|
|
new Style({
|
|
|
|
//填充色
|
|
|
|
fill: new Fill({
|
|
|
|
color: 'rgba(255, 255, 255, 0)',
|
|
|
|
}),
|
|
|
|
//边线颜色
|
|
|
|
stroke: new Stroke({
|
|
|
|
color: '#4095e5',
|
|
|
|
width: 4,
|
|
|
|
lineDash: [6],
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
point.setStyle(
|
|
|
|
new Style({
|
|
|
|
text: new Text({
|
|
|
|
//位置
|
|
|
|
textAlign: 'center',
|
|
|
|
//基准线
|
|
|
|
textBaseline: 'middle',
|
|
|
|
//文字样式
|
|
|
|
font: 'normal 14px 微软雅黑',
|
|
|
|
scale: 1.2,
|
|
|
|
//文本内容
|
|
|
|
text: value.grid_name,
|
|
|
|
//文本填充样式(即文字颜色)
|
|
|
|
fill: new Fill({ color: '#fff' }),
|
|
|
|
backgroundFill: new Fill({//背景设置必须是placement为point的形式
|
|
|
|
color: "#36a3ff"
|
|
|
|
}),
|
|
|
|
padding: [5, 5, 5, 5]
|
|
|
|
})
|
|
|
|
})
|
|
|
|
);
|
|
|
|
this.rangeSource.addFeature(polygon);
|
|
|
|
this.rangeSource.addFeature(point);
|
|
|
|
this.map.addLayer(this.rangeLayer);
|
|
|
|
},
|
|
|
|
async goSearch() {
|
|
|
|
if (!this.input) {
|
|
|
|
this.$parent.getClassify()
|
|
|
|
this.$parent.getTableList()
|
|
|
|
if (this.circleLayer) {
|
|
|
|
this.map.removeLayer(this.circleLayer)
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
const url = "search_all";
|
|
|
|
const param = {
|
|
|
|
"org_id": localStorage.getItem("agencyId") || "",
|
|
|
|
"name": this.input
|
|
|
|
}
|
|
|
|
const { data, code, msg } = await requestPostBi(
|
|
|
|
url,
|
|
|
|
{
|
|
|
|
queryParam: param
|
|
|
|
}
|
|
|
|
);
|
|
|
|
if (code == 0) {
|
|
|
|
if (data.length == 0) {
|
|
|
|
this.$message({
|
|
|
|
message: '未搜索到数据',
|
|
|
|
type: 'warning'
|
|
|
|
});
|
|
|
|
return
|
|
|
|
}
|
|
|
|
this.loadCircleLayer(data[0])
|
|
|
|
}
|
|
|
|
},
|
|
|
|
loadCircleLayer(data) {
|
|
|
|
if (!Number(data.longitude) || !Number(data.latitude)) {
|
|
|
|
this.$message({
|
|
|
|
message: '缺失坐标!',
|
|
|
|
type: 'warning'
|
|
|
|
});
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if (this.circleLayer) {
|
|
|
|
this.map.removeLayer(this.circleLayer)
|
|
|
|
}
|
|
|
|
this.circleSource = new VectorSource();
|
|
|
|
//矢量标注图层
|
|
|
|
this.circleLayer = new VectorLayer({
|
|
|
|
source: this.circleSource
|
|
|
|
});
|
|
|
|
let center = [Number(data.longitude), Number(data.latitude)]
|
|
|
|
this.$parent.getTableList(center)
|
|
|
|
this.$parent.getClassify(center)
|
|
|
|
const view = this.map.getView()
|
|
|
|
const resolutionAtEquator = view.getResolution()
|
|
|
|
const pointResolution = getPointResolution('EPSG:4326', resolutionAtEquator, center, METERS_PER_UNIT.m)
|
|
|
|
const resolutionFactor = resolutionAtEquator / pointResolution
|
|
|
|
let radius = (100 / METERS_PER_UNIT.m) * resolutionFactor
|
|
|
|
let circle = new Circle(center, radius);// 新建圆对象
|
|
|
|
let newFeature = new Feature(circle);// 新建Feature对象 并将circle传入
|
|
|
|
newFeature.setStyle(
|
|
|
|
new Style({
|
|
|
|
//填充色
|
|
|
|
fill: new Fill({
|
|
|
|
color: 'rgba(127, 131, 247, 0.5)',
|
|
|
|
}),
|
|
|
|
})
|
|
|
|
);
|
|
|
|
this.map.addLayer(this.circleLayer);
|
|
|
|
this.circleSource.addFeature(newFeature);
|
|
|
|
view.animate(
|
|
|
|
{ zoom: 16, duration: 1000 },
|
|
|
|
{ center: center, duration: 1000 });
|
|
|
|
},
|
|
|
|
getPointsCenter(points) {
|
|
|
|
points.forEach((f, i, arr) => {
|
|
|
|
arr[i] = f.join(",")
|
|
|
|
})
|
|
|
|
let point_num = points.length; //坐标点个数
|
|
|
|
let X = 0, Y = 0, Z = 0;
|
|
|
|
for (let i = 0; i < points.length; i++) {
|
|
|
|
if (points[i] == '') {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
let point = points[i].split(',');
|
|
|
|
let lat, lng, x, y, z;
|
|
|
|
lat = parseFloat(point[1]) * Math.PI / 180;
|
|
|
|
lng = parseFloat(point[0]) * Math.PI / 180;
|
|
|
|
x = Math.cos(lat) * Math.cos(lng);
|
|
|
|
y = Math.cos(lat) * Math.sin(lng);
|
|
|
|
z = Math.sin(lat);
|
|
|
|
X += x;
|
|
|
|
Y += y;
|
|
|
|
Z += z;
|
|
|
|
}
|
|
|
|
X = X / point_num;
|
|
|
|
Y = Y / point_num;
|
|
|
|
Z = Z / point_num;
|
|
|
|
|
|
|
|
let tmp_lng = Math.atan2(Y, X);
|
|
|
|
let tmp_lat = Math.atan2(Z, Math.sqrt(X * X + Y * Y));
|
|
|
|
|
|
|
|
return [tmp_lng * 180 / Math.PI, tmp_lat * 180 / Math.PI];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
</script>
|
|
|
|
|
|
|
|
<style scoped lang="scss">
|
|
|
|
#map {
|
|
|
|
position: relative;
|
|
|
|
#popup {
|
|
|
|
background-color: #0e2849;
|
|
|
|
color: #fff;
|
|
|
|
padding: 16px;
|
|
|
|
border-radius: 8px;
|
|
|
|
font-size: 18px;
|
|
|
|
.moreInfo {
|
|
|
|
margin: 12px 0;
|
|
|
|
padding: 4px;
|
|
|
|
background-color: #fff;
|
|
|
|
color: #000;
|
|
|
|
border-radius: 2px;
|
|
|
|
width: 80px;
|
|
|
|
cursor: pointer;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#popup-title {
|
|
|
|
color: #4090db;
|
|
|
|
margin-bottom: 12px;
|
|
|
|
}
|
|
|
|
.search {
|
|
|
|
position: absolute;
|
|
|
|
left: 50px;
|
|
|
|
top: 50px;
|
|
|
|
z-index: 99;
|
|
|
|
}
|
|
|
|
::v-deep {
|
|
|
|
.el-input-group__append {
|
|
|
|
background-color: #0e2849;
|
|
|
|
}
|
|
|
|
.el-input--medium {
|
|
|
|
font-size: 22px;
|
|
|
|
}
|
|
|
|
.el-input--medium .el-input__inner {
|
|
|
|
height: 45px;
|
|
|
|
line-height: 45px;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
</style>
|