实现案例
全国geojson以及城市数据可以到阿里云提供的数据服务下载datav.aliyun.com/portal/scho…
代码结构
<Openlayers id="maps">
<OlView :defaultOptions="mapStore['ol'].view">
<OlSelectLayers v-model:value="mapStore['ol'].map.layer"></OlSelectLayers>
<OlScaleLine></OlScaleLine>
<OlContextmenu @change:center="onChangeCenter" @change:zoom="onChangeZoom"></OlContextmenu>
<OlMeasure>
<OlDistance></OlDistance>
<OlArea></OlArea>
</OlMeasure>
<OlZoom></OlZoom>
<OlCompass></OlCompass>
<OlSelectMap v-model:value="mapStore.source"></OlSelectMap>
// 省市区下钻组件
<SelectArea></SelectArea>
</OlView>
</Openlayers>
城市数据转换为树结构方法
参考这边文章: juejin.cn/post/738944…
封装SelectArea.vue
<template>
<NSpace class="select">
<n-tree-select
v-model:value="code"
filterable
:options="city._map[defaultCode].children"
key-field="adcode"
label-field="name"
clearable
/>
<NButtonGroup>
<NButton :disabled="!canUndo" type="primary" @click="undo">
<RemixIcon icon="arrow-left-s-line"></RemixIcon>
</NButton>
<NButton :disabled="!canRedo" type="info" @click="redo">
<RemixIcon icon="arrow-right-s-line"></RemixIcon>
</NButton>
</NButtonGroup>
</NSpace>
</template>
<script setup>
import VectorLayer from 'ol/layer/Vector.js'
import VectorSource from 'ol/source/Vector.js'
import { GeoJSON } from 'ol/format'
import { Style, Stroke, Fill } from 'ol/style'
import Text from 'ol/style/Text.js'
import { city } from './city'
import Select from 'ol/interaction/Select.js'
import { click, pointerMove } from 'ol/events/condition.js'
let { map } = inject('openlayers')
let { view } = inject('view')
let source = new VectorSource()
let vectorLayer = new VectorLayer({
source
})
map.addLayer(vectorLayer)
let zoom = {
country: 4,
province: 7,
city: 9,
district: 11
}
let defaultCode = 100000
let code = ref(defaultCode)
// redo undo, 开源项目 vueUse提供的方法
const { undo, redo, canUndo, canRedo } = useRefHistory(code, {
capacity: 10
})
let createStyle = (feature) => {
return new Style({
text: new Text({
text: feature.values_.name,
font: '14px sans-serif',
stroke: new Stroke({
color: 'rgba(0,0,255,0.5)'
}),
fill: new Fill({
color: '#fff'
})
}),
stroke: new Stroke({
color: 'rgba(0,0,255)',
width: 2
}),
fill: new Fill({
color: 'rgba(0,0,255, 0.2)'
})
})
}
let selectStyle = (feature) => {
let style = createStyle(feature)
style.getFill().setColor('rgba(0,0,255, 0.4)')
return style
}
const onUpdateValue = async (value) => {
// 如果点击清空
if (!value) {
code.value = defaultCode
return
}
let options = city._map[code.value]
//如果点击到最底层了
if (options.level === 'district') return
source.clear()
// 请求并加载数据
let geoJSON = await getGeoJSON(code.value)
let features = new GeoJSON().readFeatures(geoJSON)
features.forEach((feature) => {
feature.setStyle(createStyle(feature))
})
source.addFeatures(features)
//移动视图
view.animate({
center: [city._map[code.value].lng, city._map[code.value].lat],
zoom: zoom[options.level],
duration: 200
})
}
watch(code, onUpdateValue, {
immediate: true // 初始化执行
})
//添加底图点击事件
const selectClick = new Select({
condition: click,
style: selectStyle,
multi: true,
layers: [vectorLayer]
})
//地图鼠标移入事件
const selectPointerMove = new Select({
condition: pointerMove,
style: selectStyle,
layers: [vectorLayer]
})
map.addInteraction(selectPointerMove)
map.addInteraction(selectClick)
selectClick.on('select', (ev) => {
if (!ev.selected.length) return
if (city._map[ev.selected[0].values_.adcode].level === 'district') return
code.value = ev.selected[0].values_.adcode
})
onUnmounted(() => {
map.removeInteraction(selectPointerMove)
map.removeInteraction(selectClick)
})
</script>
<style lang="scss" scoped>
.select {
position: absolute;
left: 20px;
top: 20px;
z-index: 99;
}
</style>