ghettovoice / vuelayers

Web map Vue components with the power of OpenLayers
https://vuelayers.github.io/
MIT License
683 stars 229 forks source link

how to create a new Source component? #494

Closed scil closed 2 years ago

scil commented 2 years ago

Is there any doc about creating new Source components?

Or I have to dive into the source code of osm-source and bingmaps-source ?

ghettovoice commented 2 years ago

Hello @scil , unfortunately no.

But actually there is nothing hard if you already know openlayers API.

For example, if you need custom XYZ source for some tile service, you can use xyzSource mixin from vuelayers/dist/mixins as base XYZ implementation. vl-source-mapbox is implemented this way, it is actually custom XYZ source not existing in the OpenLayers API https://github.com/ghettovoice/vuelayers/blob/master/src/components/mapbox-source/source.vue. For custom vector sources, you can use vectorSource mixin from vuelayers/dist/mixins. Or extend vl-source-vector component.

These are a simple cases when you only make custom Vue source component re-using OpenLayers std sources. If you need something very customized (working with internal openlayers renderers and so on), you should probably extend some openlayers source classe and then wrap it with vuelayers mixins into the vue component.

scil commented 2 years ago

Thank you very much for your advise. very helpful.

I made an XYZ source for TiandiMap(天地图)。

the mixins in vuelayers/dist is very useful.

There is the Tiandi Source which stemed from node_modules\vuelayers\src\components\mapbox-source\source.vue

<script>
import { source, urlTileSource, xyzSource } from 'vuelayers/dist/mixins'
// import { coalesce } from 'vuelayers/dist/utils'

// const TIANDI_URL_TEMPLATE = 'https://{a-c}.tiles.mapbox.com/v4/{mapId}/{z}/{x}/{y}{tileNameSuffix}.{tileFormat}?access_token={accessToken}'
const TIANDI_URL_TEMPLATE = 'http://{server}.tianditu.com/DataServer?T={tileTypeCode}&x={x}&y={y}&l={z}&tk={apiKey}'
const TIANDI_ATTRIBUTIONS = '&copy; 天地图.'

export default {
  name: 'VlSourceTiandi',
  mixins: [xyzSource],
  props: {
    /* eslint-disable vue/require-prop-types */
    // ol/source/Source
    attributions: {
      ...source.props.attributions,
      default: TIANDI_ATTRIBUTIONS
    },
    // ol/source/UrlTile
    url: {
      ...urlTileSource.props.url,
      default: TIANDI_URL_TEMPLATE
    },
    /* eslint-enable vue/require-prop-types */
    // custom
    apiKey: {
      type: String,
      required: true
    },
    mapId: {
      type: String
      // required: true
    },
    /**
     * 地形图: land
     * 文字标注: text
     */
    tileType: {
      type: String,
      default: 'land'
    }
  },
  computed: {
    /**
     * @type {string}
     */
    tileTypeCode() {
      let codes = {'land': 'ter_w', 'text': 'cva_w'}
      return codes[this.tileType]
    },
    /**
       * @type {string}
       */
    server() {
      let servers = {'land': 't4', 'text': 't3'}
      return servers[this.tileType]
    },
    /**
       * @type {string[]}
       */
    urlTokens() {
      return ['mapId', 'apiKey', 'tileTypeCode', 'server']
    }
  }
}

</script>

usage in vue :

<template>
  <div>
    <vl-map :load-tiles-while-animating="true" :load-tiles-while-interacting="true"
            data-projection="EPSG:4326" style="height: 400px">
      <vl-view :zoom.sync="zoom" :center.sync="center" :rotation.sync="rotation"></vl-view>

      <vl-geoloc @update:position="geolocPosition = $event">
        <template #default="geoloc">
          <vl-feature v-if="geoloc.position" id="position-feature">
            <vl-geom-point :coordinates="geoloc.position"></vl-geom-point>
            <vl-style-box>
              <vl-style-icon src="_media/marker.png" :scale="0.4" :anchor="[0.5, 1]"></vl-style-icon>
            </vl-style-box>
          </vl-feature>
        </template>
      </vl-geoloc>

      <vl-layer-tile id="tiandi-land">
        <vl-source-tiandi :api-key="apiKey"  tile-type="land" ></vl-source-tiandi>
      </vl-layer-tile>
      <vl-layer-tile id="tiandi-text">
        <vl-source-tiandi :api-key="apiKey"  tile-type="text" ></vl-source-tiandi>
      </vl-layer-tile>
    </vl-map>
    <div style="padding: 20px">
      Zoom: {{ zoom }}<br>
      Center: {{ center }}<br>
      Rotation: {{ rotation }}<br>
      My geolocation: {{ geolocPosition }}
    </div>
  </div>
</template>

<script>

import Vue from 'vue'
import VueLayers from 'vuelayers'
import {  Map, TileLayer, OsmSource, Geoloc } from 'vuelayers'
import 'vuelayers/dist/vuelayers.css' // needs css-loader

Vue.use(Map)
Vue.use(TileLayer)
Vue.use(OsmSource)
Vue.use(Geoloc)

import  { Source as TiandiSource} from './tiandi-source'
// import  { Source as TiandiSource} from 'vuelayers/src/components/tiandi-source'
Vue.use(TiandiSource)
Vue.component('vl-source-tiandi', TiandiSource);

Vue.use(VueLayers, {
  dataProjection: 'EPSG:4326'
})

export default {
  data() {
    return {
      apiKey: 'xxxxx',
      zoom: 4,
      center: [110, 12],
      rotation: 0,
      geolocPosition: undefined
    }
  }
}
</script>
<style scoped>

</style>