Features path

by Vladimir Vershinin

HTML

<script src="https://unpkg.com/vue@latest/dist/vue.min.js"></script>
<script src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io/en/v5.3.0/build/ol.js"></script>
<script src="https://unpkg.com/[email protected]/lib/index.umd.js"></script>
<link rel="stylesheet" href="https://unpkg.com/[email protected]/lib/style.css">
<script src="https://unpkg.com/[email protected]/turf.js"></script>
<script src="https://unpkg.com/[email protected]/dist/ol-ext.js"></script>
<div id="app">
  <button @click="animate">Animate</button>
  <vl-map ref="map" data-projection="EPSG:4326" @singleclick="onClick">
    <vl-view ref="mapView" :zoom.sync="zoom" :center.sync="center"></vl-view>
    
    <vl-layer-tile>
      <vl-source-osm />
    </vl-layer-tile>
    
    <vl-layer-vector ref="vectorLayer">
      <vl-source-vector ref="vectorSource" :features="features" />
      <vl-style-func :factory="styleFuncFactory" />
    </vl-layer-vector>
    
    <vl-overlay v-for="point in selectedPoints" :position="point.geometry.coordinates" :auto-pan="true" :key="point.id" :id="point.id">
      <div style="background: #fff">
        <p>
          Id: {{ point.id }}<br>
          Name: {{ point.properties.name }}
        </p>
      </div>
    </vl-overlay>
  </vl-map>  
</div>

CSS

html, body, #app {
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
}

JavaScript

new Vue({
  el: '#app',
  data() {
    return {
      zoom: 6,
      center: [-4, 6],
      paths: [{
        id: 1,
        points: [{
          id: 1,
          coordinates: [-5, 6],
          name: 'point 1'
        }, {
          id: 2,
          coordinates: [-5, 8],
          name: 'point 2'
        }, {
          id: 3,
          coordinates: [-3, 8],
          name: 'point 3'
        }, {
          id: 4,
          coordinates: [-3, 6],
          name: 'point 4'
        }, {
          id: 5,
          coordinates: [-1, 6],
          name: 'point 5'
        }, {
          id: 6,
          coordinates: [-1, 4],
          name: 'point 6'
        }, {
          id: 7,
          coordinates: [1, 4],
          name: 'point 7'
        }],
      }],
      selectedPoint: null,
    }
  },
  computed: {
    features() {
      return this.paths.map(p => ({
        type: 'Feature',
        id: p.id,
        geometry: {
          type: 'LineString',
          coordinates: p.points.map(p => p.coordinates),
        },
        properties: {
          points: p.points.map(p => ({
            type: 'Feature',
            id: p.id,
            properties: {
              name: p.name,
            },
            geometry: {
              type: 'Point',
              coordinates: p.coordinates,
            },
          })),
        },
      }))
    },
    selectedPoints() {
      if (!this.selectedPoint) return []
      return [this.selectedPoint]
    },
  },
  methods: {
    onClick({
      pixel,
      coordinate
    }) {
      const feature = this.$refs.map.$map.forEachFeatureAtPixel(pixel, x => x)
      if (!feature) {
        if (this.selectedPath) {
          this.selectedPath.unset('selected')
          this.selectedPath = null
        }
        this.selectedPoint = null
        return
      }

      this.selectedPath = feature
      this.selectedPoint = turf.nearest(
        turf.point(coordinate),
        turf.featureCollection(feature.get('points')),
      )
     ...