view client/src/store/map.js @ 1237:74562dc29e10

refactored drawtool The map interactions (ol/interaction/Draw) were previously always removed and re-created. Now there are created and added to the map once and their active flag is used to toggle the tools. Results in cleaner code and easier separation of the buttons in the future.
author Markus Kottlaender <markus@intevation.de>
date Tue, 20 Nov 2018 13:03:24 +0100
parents 49740dcba52c
children c14353e2cdb9
line wrap: on
line source

/*
 * This is Free Software under GNU Affero General Public License v >= 3.0
 * without warranty, see README.md and license for details.
 * 
 * SPDX-License-Identifier: AGPL-3.0-or-later
 * License-Filename: LICENSES/AGPL-3.0.txt
 * 
 * Copyright (C) 2018 by via donau 
 *   – Österreichische Wasserstraßen-Gesellschaft mbH
 * Software engineering by Intevation GmbH
 * 
 * Author(s):
 * Thomas Junk <thomas.junk@intevation.de>
 */

//import { HTTP } from "../lib/http";

import TileWMS from "ol/source/TileWMS.js";
import { Tile as TileLayer, Vector as VectorLayer } from "ol/layer.js";
import OSM from "ol/source/OSM";
import {
  Icon,
  Stroke,
  Style,
  Fill,
  Text,
  Circle as CircleStyle
} from "ol/style.js";
import VectorSource from "ol/source/Vector.js";
import Point from "ol/geom/Point.js";
import { bbox as bboxStrategy } from "ol/loadingstrategy";
import { HTTP } from "../application/lib/http";
import { fromLonLat } from "ol/proj";

export default {
  namespaced: true,
  state: {
    openLayersMap: null,
    identifiedFeatures: [], // map features identified by clicking on the map
    currentMeasurement: null, // distance or area from drawTool
    lineTool: null, // open layers interaction object (Draw)
    polygonTool: null, // open layers interaction object (Draw)
    cutTool: null, // open layers interaction object (Draw)
    layers: [
      {
        name: "Open Streetmap",
        data: new TileLayer({
          source: new OSM()
        }),
        isVisible: true,
        showInLegend: true
      },
      {
        name: "Inland ECDIS chart Danube",
        data: new TileLayer({
          source: new TileWMS({
            preload: 1,
            url: "https://demo.d4d-portal.info/wms",
            params: { LAYERS: "d4d", VERSION: "1.1.1", TILED: true }
          })
        }),
        isVisible: true,
        showInLegend: true
      },
      {
        name: "Fairway Dimensions",
        data: new VectorLayer({
          source: new VectorSource(),
          style: function(feature) {
            return [
              new Style({
                stroke: new Stroke({
                  color: "rgba(0, 0, 255, 1.0)",
                  width: 2
                })
              }),
              new Style({
                text: new Text({
                  font: 'bold 12px "Open Sans", "sans-serif"',
                  placement: "line",
                  fill: new Fill({
                    color: "black"
                  }),
                  text: "LOS: " + feature.get("level_of_service").toString()
                  //, zIndex: 10
                })
              })
            ];
          }
        }),
        isVisible: true,
        showInLegend: true
      },
      {
        name: "Waterway Area, named",
        data: new VectorLayer({
          source: new VectorSource({
            strategy: bboxStrategy
          }),
          style: new Style({
            stroke: new Stroke({
              color: "rgba(0, 132, 0, 1)",
              width: 2
            })
          })
        }),
        isVisible: false,
        showInLegend: true
      },
      {
        name: "Waterway Area",
        data: new VectorLayer({
          source: new VectorSource({
            strategy: bboxStrategy
          }),
          style: new Style({
            stroke: new Stroke({
              color: "rgba(0, 102, 0, 1)",
              width: 2
            })
          })
        }),
        isVisible: true,
        showInLegend: true
      },
      {
        name: "Waterway Axis",
        data: new VectorLayer({
          source: new VectorSource({
            strategy: bboxStrategy
          }),
          style: new Style({
            stroke: new Stroke({
              color: "rgba(0, 0, 255, .5)",
              lineDash: [5, 5],
              width: 2
            })
          })
        }),
        isVisible: true,
        showInLegend: true
      },
      {
        name: "Distance marks",
        forLegendStyle: { point: true, resolution: 8 },
        data: new VectorLayer({
          source: new VectorSource({
            strategy: bboxStrategy
          })
        }),
        isVisible: false,
        showInLegend: true
      },
      {
        name: "Bottlenecks",
        data: new VectorLayer({
          source: new VectorSource({
            strategy: bboxStrategy
          }),
          style: new Style({
            stroke: new Stroke({
              color: "rgba(230, 230, 10, .8)",
              width: 4
            }),
            fill: new Fill({
              color: "rgba(230, 230, 10, .3)"
            })
          })
        }),
        isVisible: true,
        showInLegend: true
      },
      {
        name: "Bottleneck isolines",
        data: new TileLayer({
          source: new TileWMS({
            preload: 0,
            projection: "EPSG:3857",
            url: window.location.origin + "/api/internal/wms",
            params: {
              LAYERS: "sounding_results_contour_lines_geoserver",
              VERSION: "1.1.1",
              TILED: true
            },
            tileLoadFunction: function(tile, src) {
              // console.log("calling for", tile, src);
              HTTP.get(src, {
                headers: {
                  "X-Gemma-Auth": localStorage.getItem("token")
                },
                responseType: "blob"
              }).then(response => {
                tile.getImage().src = URL.createObjectURL(response.data);
              });
            } // TODO  tile.setState(TileState.ERROR);
          })
        }),
        isVisible: false,
        showInLegend: true
      },
      {
        name: "Distance marks, Axis",
        forLegendStyle: { point: true, resolution: 8 },
        data: new VectorLayer({
          source: new VectorSource({
            strategy: bboxStrategy
          }),
          style: function(feature, resolution) {
            if (resolution < 10) {
              var s = new Style({
                image: new CircleStyle({
                  radius: 5,
                  fill: new Fill({ color: "rgba(255, 0, 0, 0.1)" }),
                  stroke: new Stroke({ color: "blue", width: 1 })
                })
              });
              if (resolution < 6) {
                s.setText(
                  new Text({
                    offsetY: 12,
                    font: '10px "Open Sans", "sans-serif"',
                    fill: new Fill({
                      color: "black"
                    }),
                    text: (feature.get("hectometre") / 10).toString()
                  })
                );
              }
              return s;
            } else {
              return [];
            }
          }
        }),
        isVisible: true,
        showInLegend: true
      },
      {
        name: "Draw Tool",
        data: new VectorLayer({
          source: new VectorSource({ wrapX: false }),
          style: function(feature) {
            // adapted from OpenLayer's LineString Arrow Example
            var geometry = feature.getGeometry();
            var styles = [
              // linestring
              new Style({
                stroke: new Stroke({
                  color: "#369aca",
                  width: 2
                })
              })
            ];

            if (geometry.getType() === "LineString") {
              geometry.forEachSegment(function(start, end) {
                var dx = end[0] - start[0];
                var dy = end[1] - start[1];
                var rotation = Math.atan2(dy, dx);
                // arrows
                styles.push(
                  new Style({
                    geometry: new Point(end),
                    image: new Icon({
                      // we need to make sure the image is loaded by Vue Loader
                      src: require("../application/assets/linestring_arrow.png"),
                      // fiddling with the anchor's y value does not help to
                      // position the image more centered on the line ending, as the
                      // default line style seems to be slightly uncentered in the
                      // anti-aliasing, but the image is not placed with subpixel
                      // precision
                      anchor: [0.75, 0.5],
                      rotateWithView: true,
                      rotation: -rotation
                    })
                  })
                );
              });
            }
            return styles;
          }
        }),
        isVisible: true,
        showInLegend: false
      },
      {
        name: "Cut Tool",
        data: new VectorLayer({
          source: new VectorSource({ wrapX: false }),
          style: function(feature) {
            // adapted from OpenLayer's LineString Arrow Example
            var geometry = feature.getGeometry();
            var styles = [
              // linestring
              new Style({
                stroke: new Stroke({
                  color: "#333333",
                  width: 2,
                  lineDash: [7, 7]
                })
              })
            ];

            if (geometry.getType() === "LineString") {
              geometry.forEachSegment(function(start, end) {
                var dx = end[0] - start[0];
                var dy = end[1] - start[1];
                var rotation = Math.atan2(dy, dx);
                // arrows
                styles.push(
                  new Style({
                    geometry: new Point(end),
                    image: new Icon({
                      // we need to make sure the image is loaded by Vue Loader
                      src: require("../application/assets/linestring_arrow_grey.png"),
                      // fiddling with the anchor's y value does not help to
                      // position the image more centered on the line ending, as the
                      // default line style seems to be slightly uncentered in the
                      // anti-aliasing, but the image is not placed with subpixel
                      // precision
                      anchor: [0.75, 0.5],
                      rotateWithView: true,
                      rotation: -rotation
                    })
                  })
                );
              });
            }
            return styles;
          }
        }),
        isVisible: true,
        showInLegend: false
      }
    ]
  },
  getters: {
    layersForLegend: state => {
      return state.layers.filter(layer => layer.showInLegend);
    },
    getLayerByName: state => name => {
      return state.layers.find(layer => layer.name === name);
    }
  },
  mutations: {
    toggleVisibility: (state, layer) => {
      state.layers[layer].isVisible = !state.layers[layer].isVisible;
      state.layers[layer].data.setVisible(state.layers[layer].isVisible);
    },
    setOpenLayersMap: (state, map) => {
      state.openLayersMap = map;
    },
    setIdentifiedFeatures: (state, identifiedFeatures) => {
      state.identifiedFeatures = identifiedFeatures;
    },
    setCurrentMeasurement: (state, measurement) => {
      state.currentMeasurement = measurement;
    },
    lineTool: (state, lineTool) => {
      state.lineTool = lineTool;
    },
    polygonTool: (state, polygonTool) => {
      state.polygonTool = polygonTool;
    },
    cutTool: (state, cutTool) => {
      state.cutTool = cutTool;
    },
    moveMap: (state, { coordinates, zoom, preventZoomOut }) => {
      let view = state.openLayersMap.getView();
      const currentZoom = view.getZoom();
      view.animate({
        zoom: preventZoomOut ? Math.max(zoom, currentZoom) : zoom,
        center: fromLonLat(coordinates, view.getProjection()),
        duration: 700
      });
    }
  }
};