官网demo地址:
Custom Drag-and-Drop (KMZ)
https://openlayers.org/en/latest/examples/drag-and-drop-custom-kmz.html
这个示例展示了如何拖拽文件到地图上并实时解析成矢量图层
首先来看看什么是KMZ
KMZ和KML有什么区别?
KML格式:
- 文件格式:KML文件是基于XML的纯文本文件。
- 文件扩展名:.kml
- 内容:
- 包含地理要素和数据,例如点、线、面、图像叠加以及其他地理信息。
- 结构清晰,可读性强,可以用文本编辑器打开查看和编辑。
- 大小:因为是纯文本文件,KML文件可能会变得非常大,尤其是包含大量详细地理数据时。
KMZ 格式:
- 文件格式:KMZ文件是KML文件的压缩版本。
- 文件扩展名:.kmz
- 内容:
- KMZ文件是一个ZIP压缩包,里面包含一个或多个KML文件以及其他相关资源(如图片、模型等)。
- 主KML文件通常命名为
doc.kml
。 - 可以包含多个文件和文件夹,使得相关资源可以一起分发。
- 大小:由于KMZ是压缩文件,通常比对应的KML文件要小,便于分发和下载。
- 多媒体资源:KMZ文件可以打包包括图片、3D模型(如Collada文件)等多媒体资源,这些资源可以在解压后保持相对路径关系。
由此可知,KMZ格式转化成KML格式只需要进行解压,所以这里创建了一个KMZ类继承了KML,重写了readFeature和readFeatures两个方法,在解析之前增加了解压操作。
class KMZ extends KML {
constructor(opt_options) {
const options = opt_options || {};
options.iconUrlFunction = getKMLImage;
super(options);
}
getType() {
return "arraybuffer";
}
readFeature(source, options) {
const kmlData = getKMLData(source);
return super.readFeature(kmlData, options);
}
readFeatures(source, options) {
const kmlData = getKMLData(source);
return super.readFeatures(kmlData, options);
}
}
解压用到了 JSZip库
const zip = new JSZip();
function getKMLData(buffer) {
let kmlData;
zip.load(buffer);
const kmlFile = zip.file(/\.kml$/i)[0];
if (kmlFile) {
kmlData = kmlFile.asText();
}
return kmlData;
}
我们也来下载一下,官网用的版本是2.6.1,如果下最新版用法可能会不一样,所以下载的时候指定一下版本号。
npm i jszip@2.6.1
import JSZip from "jszip";
然后是拖拽操作,创建了一个交互DragAndDrop, defaultInteractions()
就是获取了OpenLayers提供的默认交互集合,包括地图的平移、缩放、双击缩放、鼠标滚轮缩放等基本交互。
const dragAndDropInteraction = new DragAndDrop({
formatConstructors: [KMZ, GPX, GeoJSON, IGC, KML, TopoJSON],
});
const map = new Map({
interactions: defaultInteractions().extend([dragAndDropInteraction]),
layers: [
new TileLayer({
source: new OSM(),
}),
],
target: "map",
view: new View({
center: [0, 0],
zoom: 2,
}),
});
监听addfeatures事件,当有新feature添加之后会增加一个矢量图层,并且让地图定位到feature的位置。
dragAndDropInteraction.on("addfeatures", function (event) {
const vectorSource = new VectorSource({
features: event.features,
});
map.addLayer(
new VectorLayer({
source: vectorSource,
})
);
map.getView().fit(vectorSource.getExtent());
});
鼠标在地图上经过或点击就会获取feature上的属性展示到表格中
map.on("pointermove", function (evt) {
if (evt.dragging) {
return;
}
const pixel = map.getEventPixel(evt.originalEvent);
displayFeatureInfo(pixel);
});
map.on("click", function (evt) {
displayFeatureInfo(evt.pixel);
});
能展示的前提是数据里有这些属性。
const displayFeatureInfo = function (pixel) {
const features = [];
map.forEachFeatureAtPixel(pixel, function (feature) {
features.push(feature);
});
if (features.length > 0) {
const info = [];
let i, ii;
for (i = 0, ii = features.length; i < ii; ++i) {
const description =
features[i].get("description") ||
features[i].get("name") ||
features[i].get("_name") ||
features[i].get("layer");
if (description) {
info.push(description);
}
}
document.getElementById("info").innerHTML =
info.join("<br/>") || " ";
} else {
document.getElementById("info").innerHTML = " ";
}
};
接下来把官网提供的KMZ样例数据下载下来就可以测试了
下载下来是这样的
然后拖到地图上就可以显示了。
除了KML数据我们也下一个GeoJson数据试试。
到这个网站
选择下载文件
下载之后是json文件,手动把文件后缀名改成GeoJson。
然后拖到地图上即可解析。
完整代码:
<template>
<div class="box">
<h1>拖拽KMZ, GPX, GeoJSON, IGC, KML, TopoJSON文件到地图上直接绘制矢量图层</h1>
<div id="map"></div>
<br />
<div id="info"> </div>
</div>
</template>
<script>
import Map from "ol/Map.js";
import View from "ol/View.js";
import {
DragAndDrop,
defaults as defaultInteractions,
} from "ol/interaction.js";
import { GPX, GeoJSON, IGC, KML, TopoJSON } from "ol/format.js";
import { OSM, Vector as VectorSource } from "ol/source.js";
import { Tile as TileLayer, Vector as VectorLayer } from "ol/layer.js";
import JSZip from "jszip";
export default {
name: "",
components: {},
data() {
return {
map: null,
};
},
computed: {},
created() {},
mounted() {
const zip = new JSZip();
function getKMLData(buffer) {
let kmlData;
zip.load(buffer);
const kmlFile = zip.file(/\.kml$/i)[0];
if (kmlFile) {
kmlData = kmlFile.asText();
}
return kmlData;
}
function getKMLImage(href) {
const index = window.location.href.lastIndexOf("/");
if (index !== -1) {
const kmlFile = zip.file(href.slice(index + 1));
if (kmlFile) {
return URL.createObjectURL(new Blob([kmlFile.asArrayBuffer()]));
}
}
return href;
}
class KMZ extends KML {
constructor(opt_options) {
const options = opt_options || {};
options.iconUrlFunction = getKMLImage;
super(options);
}
getType() {
return "arraybuffer";
}
readFeature(source, options) {
const kmlData = getKMLData(source);
return super.readFeature(kmlData, options);
}
readFeatures(source, options) {
const kmlData = getKMLData(source);
return super.readFeatures(kmlData, options);
}
}
const dragAndDropInteraction = new DragAndDrop({
formatConstructors: [KMZ, GPX, GeoJSON, IGC, KML, TopoJSON],
});
const map = new Map({
interactions: defaultInteractions().extend([dragAndDropInteraction]),
layers: [
new TileLayer({
source: new OSM(),
}),
],
target: "map",
view: new View({
center: [0, 0],
zoom: 2,
}),
});
dragAndDropInteraction.on("addfeatures", function (event) {
const vectorSource = new VectorSource({
features: event.features,
});
map.addLayer(
new VectorLayer({
source: vectorSource,
})
);
map.getView().fit(vectorSource.getExtent());
});
const displayFeatureInfo = function (pixel) {
const features = [];
map.forEachFeatureAtPixel(pixel, function (feature) {
features.push(feature);
});
if (features.length > 0) {
const info = [];
let i, ii;
for (i = 0, ii = features.length; i < ii; ++i) {
const description =
features[i].get("description") ||
features[i].get("name") ||
features[i].get("_name") ||
features[i].get("layer");
if (description) {
info.push(description);
}
}
document.getElementById("info").innerHTML =
info.join("<br/>") || " ";
} else {
document.getElementById("info").innerHTML = " ";
}
};
map.on("pointermove", function (evt) {
if (evt.dragging) {
return;
}
const pixel = map.getEventPixel(evt.originalEvent);
displayFeatureInfo(pixel);
});
map.on("click", function (evt) {
displayFeatureInfo(evt.pixel);
});
},
methods: {},
};
</script>
<style lang="scss" scoped>
#map {
width: 100%;
height: 500px;
}
.box {
height: 100%;
}
#info {
width: 100%;
height: 24rem;
overflow: scroll;
display: flex;
align-items: baseline;
border: 1px solid black;
justify-content: flex-start;
}
</style>