openlayers 加载geoJSON,实现省市区下钻

583 阅读1分钟

实现案例

2024-07-08_15-40-05.gif

全国geojson以及城市数据可以到阿里云提供的数据服务下载datav.aliyun.com/portal/scho…

仓库地址 gitee.com/Mr-ZhouCB/v…

代码结构

<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>

城市数据转换为树结构方法

image.png

参考这边文章: 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>