1.安装
npm i ol -S
2.介绍
OpenLayers 的核心组件是地图 ( ol/Map
)。它被渲染到一个target
容器(例如div
网页上包含地图的元素)。所有地图属性都可以在构建时进行配置,也可以使用 setter 方法进行配置,例如setTarget()
.
地图不负责地图的中心、缩放级别和投影等事项。相反,这些是ol/View
实例的属性。
为了获取层的远程数据,OpenLayers 使用ol/source/Source
子类。这些可用于 OpenStreetMap 或 Bing 等免费和商业地图切片服务、WMS 或 WMTS 等 OGC 源,以及 GeoJSON 或 KML 等格式的矢量数据。
layer是来自source
. OpenLayers 有四种基本类型的层:
ol/layer/Tile
- 渲染在按特定分辨率的缩放级别组织的网格中提供平铺图像的源。ol/layer/Image
- 渲染以任意范围和分辨率提供地图图像的源。ol/layer/Vector
- 在客户端渲染矢量数据。ol/layer/VectorTile
- 渲染作为矢量图块提供的数据
3.使用
话不说多,直接贴代码,这里面使用了Vue3,Element UI
中国JSON数据:areaGeo参数【js/Map/china.json · 梦_阿飞/allStaticResources - Gitee.com】
河南JSON数据:areaCity参数【js/Map/city.json · 梦_阿飞/allStaticResources - Gitee.com】
渲染数据:polymerization 【js/Map/polymerization.json · 梦_阿飞/allStaticResources - Gitee.com】
<template>
<div class="MonitoringData">
<div id="Map" ref="map"></div>
<div id="popup" class="ol-popup">
<div id="popup-closer" class="ol-popup-closer">X</div>
<div id="popup-detail" class="ol-popup-detail">查看详情</div>
<div id="popup-content" class="popup-content">
</div>
</div>
<div class="map-left">
<div class="text">(仅支持郑州,开封,尉氏..)</div>
<div class="flexbox">
<el-input v-model="form.input" placeholder="请输入城市名" clearable style="width:150px;margin-right:15px;" />
<el-button type="primary" @click="look">查询</el-button>
</div>
<div class="list">
<li v-for="(item,index) in form.list " :key='index'>
<div class="fx-space-between-center li">
<div>{
{item.name}}</div>
<div>{
{item.record}}</div>
</div>
<div class="text-li">{
{item.text}}</div>
</li>
</div>
</div>
<div class="model fx-center" v-show="modelSatus.status">
<div class="">
<div>我是无情的白色弹窗</div>
<el-button type="primary" @click="modelSatus.status=false">关闭</el-button>
</div>
</div>
</div>
</template>
<script>
import "ol/ol.css";
import TileLayer from "ol/layer/Tile";
import VectorLayer from "ol/layer/Vector";
import VectorSource from "ol/source/Vector";
import Cluster from "ol/source/Cluster";
import XYZ from "ol/source/XYZ";
import { Map, View, Feature, Overlay } from "ol";
import { Style, Stroke, Text, Fill, Circle } from "ol/style";
import { Polygon, MultiPolygon, Point } from "ol/geom";
import { defaults as defaultControls } from "ol/control";
import { fromLonLat } from "ol/proj";
import areaGeo from "@/utils/china.json";
import areaCity from "@/utils/city.json";
import polymerization from "@/utils/polymerization.json";
import { onMounted, reactive } from "vue";
export default {
name: "MapData",
setup() {
let map = null;
let areaLayer = null;
let popupData = null;
let modelSatus = reactive({
status: false,
});
let form = reactive({
input: "",
list: polymerization.points,
});
const initMap = () => {
map = new Map({
target: "Map",
controls: defaultControls({
zoom: true,
}).extend([]),
layers: [
new TileLayer(
{
source: new XYZ({
url: "http://map.geoq.cn/ArcGIS/rest/services/ChinaOnlineCommunity/MapServer/tile/{z}/{y}/{x}",
}),
},
{ zoomOffset: 1 }
),
],
view: new View({
// center: fromLonLat([108.522097, 37.272848]),
center: fromLonLat([115.153576, 32.287459]),
zoom: 7,
maxZoom: 15,
minZoom: 2,
}),
});
};
/**
* Popup 弹窗
*/
const addPopup = () => {
// 使用变量存储弹窗所需的 DOM 对象
var container = document.getElementById("popup");
var closer = document.getElementById("popup-closer");
var content = document.getElementById("popup-content");
var detail = document.getElementById("popup-detail");
// 创建一个弹窗 Overlay 对象
const overlay = new Overlay({
element: container, //绑定 Overlay 对象和 DOM 对象的
autoPan: true, // 定义弹出窗口在边缘点击时候可能不完整 设置自动平移效果
autoPanAnimation: {
duration: 250, //自动平移效果的动画时间 9毫秒
},
});
// 将弹窗添加到 map 地图中
map.addOverlay(overlay);
/**
* 添加单击响应函数来处理弹窗动作
*/
map.on("click", function (evt) {
var feature = map.forEachFeatureAtPixel(evt.pixel, function (feature) {
return feature;
});
if (feature) {
let value = feature.get("features");
popupData = value[0].values_;
if (value.length < 2) {
content.innerHTML = `
<p style="text-align: center;font-size:18px;font-weight:bold;">${popupData.name}-${popupData.value}</p>
<div style="display:flex;justify-content:space-between;">
<div><span>攻击:</span>${popupData.attack}</div>
<div><span>法伤:</span>${popupData.magic}</div>
</div>
<div style="display:flex;justify-content:space-between;">
<div><span>暴击:</span>${popupData.crit}</div>
<div><span>战绩:</span>${popupData.record}</div>
</div>
<div>${popupData.text}</div>
`;
overlay.setPosition(evt.coordinate); //把 overlay 显示到指定的 x,y坐标
} else {
overlay.setPosition(undefined);
}
} else {
overlay.setPosition(undefined);
}
});
/**
* 为弹窗添加一个响应关闭的函数
*/
closer.onclick = function () {
overlay.setPosition(undefined);
closer.blur();
return false;
};
detail.onclick = function () {
modelSatus.status = true;
overlay.setPosition(undefined);
};
};
/**
* 设置区域
*/
const addArea = (geo = [], city = []) => {
if (geo.length == 0) return false;
let areaFeature = null; // 国家
let provinceData = null; // 城市
// 设置图层
areaLayer = new VectorLayer({
source: new VectorSource({
features: [],
}),
});
// 添加图层
map.addLayer(areaLayer);
// ---国家
geo.forEach((g) => {
let lineData = g.features[0];
if (lineData.geometry.type == "MultiPolygon") {
areaFeature = new Feature({
geometry: new MultiPolygon(lineData.geometry.coordinates).transform(
"EPSG:4326",
"EPSG:3857"
),
});
} else if (lineData.geometry.type == "Polygon") {
areaFeature = new Feature({
geometry: new Polygon(lineData.geometry.coordinates).transform(
"EPSG:4326",
"EPSG:3857"
),
});
}
});
// 渲染国家---
areaFeature.setStyle(
new Style({
// fill: new Fill({ color: "#4e98f444" }),
stroke: new Stroke({
width: 3,
color: [71, 137, 227, 1],
}),
})
);
areaLayer.getSource().addFeatures([areaFeature]);
// 城市
city[0].features.forEach((item) => {
let lineData = item;
provinceData = new Feature({
geometry: new MultiPolygon(lineData.geometry.coordinates).transform(
"EPSG:4326",
"EPSG:3857"
),
});
provinceData.setStyle(
new Style({
stroke: new Stroke({
width: 2,
color: "blue",
}),
})
);
//需要s设置属性进去,然后在需要的时候才可以get到
provinceData.set("name2", lineData.properties.name);
areaLayer.getSource().addFeatures([provinceData]);
});
//
};
// 聚合
const addCluster = (clusterData, points) => {
let source = new VectorSource();
let clusterSource = new Cluster({
distance: parseInt(20, 10),
source: source,
});
let layers = new VectorLayer({
source: clusterSource,
style: clusterStyle.call(this),
});
map.addLayer(layers);
for (let index = 0; index < clusterData.length; index++) {
const ele = clusterData[index];
points.forEach((e) => {
if (ele.name == e.name) {
let point = fromLonLat(ele.center);
var f = new Feature({
geometry: new Point(point),
});
for (let key in e) {
f.set(key, e[key]);
}
source.addFeature(f);
}
});
}
};
const clusterStyle = () => {
return (feature) => {
var total = 0;
var name = null;
feature.get("features").forEach((value) => {
total += value.get("value");
name = value.get("name");
});
var size = feature.get("features").length;
var style = new Style({
image: new Circle({
fill: new Fill({
color: "red",
}),
stroke: new Stroke({
color: "red",
width: size != 1 ? Number(12.25) : Number(1),
}),
radius: size != 1 ? 10 : 5,
}),
text: new Text({
text: size != 1 ? total.toString() : name, //lineData.properties.name
fill: new Fill({
color: size != 1 ? "#fff" : "#303133",
}),
font:
size != 1
? "12px Microsoft YaHei bold"
: "18px Microsoft YaHei bold",
offsetY: size != 1 ? 0 : 20,
}),
});
return style;
};
};
const look = () => {
//
let conter = polymerization.clusterData.filter(
(e) => e.name == form.input
);
if (form.input == "") {
form.list = polymerization.points;
} else {
form.list = polymerization.points.filter((e) => e.name == form.input);
}
let view = map.getView();
if (conter.length > 0) {
view.setZoom(18);
view.animate({
center: fromLonLat(conter[0].center),
duration: 3,
});
} else {
view.setZoom(7);
view.animate({
center: fromLonLat([115.153576, 32.287459]),
duration: 3,
});
}
};
onMounted(() => {
initMap(); //初始化地图方法
addArea(areaGeo, areaCity); //添加区域图层方法
addCluster(polymerization.clusterData, polymerization.points); //数据聚合
addPopup(); //弹窗
});
return {
initMap,
addArea,
map,
areaLayer,
addCluster,
clusterStyle,
addPopup,
popupData,
modelSatus,
look,
form,
};
},
};
</script>
<style scoped lang="scss">
.MonitoringData {
height: 100%;
position: relative;
.model {
position: absolute;
height: 100%;
width: 100%;
background: #fff;
bottom: 0;
left: 0;
text-align: center;
}
.map-left {
width: 300px;
height: 70%;
position: absolute;
background: #fff;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2);
position: absolute;
top: 20px;
left: 20px;
overflow: auto;
padding: 10px;
.text {
font-size: 12px;
color: #cccccc;
padding-bottom: 10px;
}
.list {
width: 100%;
margin-top: 15px;
li {
border-bottom: 1px solid #e3e3e3;
padding: 5px 0;
}
.li {
width: 100%;
font-size: 16px;
color: #868585;
padding-bottom: 5px;
}
.text-li {
background: linear-gradient(red, green, blue);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
}
}
}
#Map {
width: 100%;
height: 100%;
.ol-popup {
position: absolute;
background-color: white;
-webkit-filter: drop-shadow(0 1px 4px rgba(0, 0, 0, 0.2));
filter: drop-shadow(0 1px 4px rgba(0, 0, 0, 0.2));
padding: 15px;
border-radius: 10px;
border: 1px solid #cccccc;
bottom: 12px;
left: -50px;
}
.ol-popup:after,
.ol-popup:before {
top: 100%;
border: solid transparent;
content: " ";
height: 0;
width: 0;
position: absolute;
pointer-events: none;
}
.ol-popup:after {
border-top-color: white;
border-width: 10px;
left: 48px;
margin-left: -10px;
}
.ol-popup:before {
border-top-color: #cccccc;
border-width: 11px;
left: 48px;
margin-left: -11px;
}
.ol-popup-closer {
text-decoration: none;
position: absolute;
top: 2px;
right: 8px;
cursor: pointer;
}
.ol-popup-detail {
text-decoration: none;
position: absolute;
top: 4px;
left: 8px;
cursor: pointer;
}
.popup-content {
width: 400px;
}
}
</style>