Использование OpenLayers в React - PullRequest
0 голосов
/ 11 апреля 2020

Я студент, который создает большой проект, пытаясь отобразить данные и информацию на картах, используя Openlayers и React. На данный момент я создал двухстраничный проект basi c, в котором карта является центральной частью приложения. Для каждой страницы у меня есть один основной Компонент, который содержит боковую панель, Компонент Viewer, который содержит саму карту и некоторые дополнительные Компоненты, специфицирующие c для этой страницы.

Класс Viewer - это специальный Компонент, для которого я написал каждая страница Этот компонент обрабатывает все взаимодействия пользователя со страницей. Он также отправляет и получает информацию от основного компонента. Я решил создать отдельный класс Viewer для каждой страницы из-за различных способов работы каждой страницы. Они могут загружаться разными вещами или обрабатывать взаимодействия совершенно по-разному. Если бы у меня был один класс Viewer, который можно использовать на каждой странице, он стал бы большим и имел бы много операторов if, чтобы проверить, какой код запускать при обработке каждой отдельной страницы.

Вот пример просмотра домашней страницы. В конструкторе я создаю карту и добавляю несколько ссылок для всплывающего окна, которое появляется, когда пользователь нажимает на одну из функций на карте. Это всплывающее окно показывает некоторую базовую информацию о функции и позволяет пользователю добавить ее в свой список функций.

  • В ComponentDidMount я сначала заставляю класс Map добавить слой границ и даю функцию для вызова при нажатии на одну из функций этого слоя. Я также создаю окно наложения для всплывающего окна на карту. Это будет показано при нажатии на элемент.

  • resetMapLayers - это функция, которая будет вызываться при каждом рендеринге. Здесь я проверяю карту, какой фоновый слой использовать, и должен ли он отображать верхний слой или нет.

  • featureSelected () - это функция, которая обрабатывает событие щелчка
    особенность верхнего слоя. Это создаст всплывающее окно с базисной c
    информацией о функции.

  • closePopup будет вызвано после закрытия всплывающего окна. Это
    заставит карту отказаться от выбранной функции. В противном случае он останется выбранным, когда всплывающее окно будет закрыто. Он также удалит Overlay (всплывающее окно) с экрана.

  • addFeature () - это функция, которая будет вызываться, когда пользователь решит добавить эту функцию в свой список функций. Это может сделать пользователь во всплывающем окне этой функции

.

import React, { Component } from "react";
import { connect } from "react-redux";

import Map from "../Map/Map";
import "ol/ol.css";
import styles from "./Viewer.module.scss";
import Overlay from "ol/Overlay";
import Button from "../UI/Button/Button";

class MainViewer extends Component {
  constructor(props) {
    super(props);
    Map.createNewMap();

    this.popup = React.createRef();
    this.popupContent = React.createRef();
  }

  componentDidMount() {
    Map.addBoundriesLayer(this.featureSelected);
    Map.map.setTarget("map");
    let container = this.popup.current;
    let overlay = new Overlay({
      element: container,
      autoPan: true,
      autoPanAnimation: {
        duration: 250,
      },
    });
    this.overlay = overlay;
    Map.map.addOverlay(overlay);
  }

  resetMapLayers() {
    Map.setBackgroundTileLayer(this.props.type);
    Map.togglePlotBoundriesLayers(this.props.plotBoundriesState);
  }

  featureSelected = (event, select) => {
    if (event.selected[0]) {
      this.selectedFeature = event.selected[0];
      let selectedFeature = {
        id: event.selected[0].id_,
        gewasgroepnaam: event.selected[0].getProperties().GEWASGROEP,
        gewasnaam: event.selected[0].getProperties().LBLHFDTLT,
        oppervlak: (event.selected[0].getProperties().OPPERVL / 10000).toFixed(
          2
        ),
        coords: event.selected[0].getProperties().geometry.extent_,
      };

      let content = this.popupContent.current;
      content.innerHTML =
        "<p><strong>Name: </strong>" +
        selectedFeature.name +
        "</p>" +
        "<p><strong>Exact Name: </strong>" +
        selectedFeature.exactName +
        "</p>" +
        "<p><strong>Area: </strong>" +
        selectedFeature.area +
        " ha</p>";
      this.overlay.setPosition(event.mapBrowserEvent.coordinate);
    }
  };

  closePopup() {
    this.overlay.setPosition(undefined);
    Map.clearSelect();
    return false;
  }

  addFeature() {
    this.overlay.setPosition(undefined);
    Map.clearSelect();
    this.props.featureAddedHandler(this.selectedFeature);
  }

  render() {
    this.resetMapLayers();
    return (
      <div>
        <div id="map" className={styles.Map}></div>
        <div ref={this.popup} className={styles.OlPopup}>
          <div className={styles.OlPopupButtonsDiv}>
            <Button
              btnType="Danger"
              className={[styles.PopupButton, styles.ClosePopupButton].join(
                " "
              )}
              clicked={() => this.closePopup()}
            >
              Annuleer
            </Button>
            <Button
              btnType="Success"
              className={[styles.PopupButton, styles.AddPopupButton].join(" ")}
              clicked={() => this.addFeature()}
            >
              Voeg Toe
            </Button>
          </div>
          <div ref={this.popupContent}></div>
        </div>
      </div>
    );
  }
}

const mapStateToProps = (state) => {
  return {
    type: state.mapDetails.type,
    plotBoundriesState: state.mapDetails.state,
  };
};

export default connect(mapStateToProps)(MainViewer);

Класс Map содержит фактическую карту. Он справляется со всеми указанными c картами. Это позволяет добавлять слои на карту, создавать начальную карту, добавлять взаимодействия карты, ...

  • В конструкторе я вызываю методы для инициализации создания базовой c карты с фон TileLayer.

  • Методы createMap и createNewMap позволяют мне создать сам объект карты.

  • createBackgroundLayerGroups () создает элемент Tilelayer, который является фоновый слой. Это может быть OSM или Bing Maps. Свойство видимости позволяет мне указать, какой из них показывать.

  • clearAllBoundriesLayers () удаляет все слои границ, которые можно поместить поверх слоя листов. Он удаляет каждый слой и очищает выбранные взаимодействия, которые были добавлены к этим слоям. Я делаю это так, чтобы при смене страницы слои удалялись.

  • addBoundriesLayer давайте мне установим и добавим слой границ. Это векторный слой, который будет помещен поверх TileLayer.

  • addUsersPlotBoundriesLayer делает то же самое, что и addBoundriesLayer, но делает это для всех слоев, которые есть у пользователя. Эта функция будет вызываться на другой странице, которая отображает только функции пользователя.

  • setExtentOfMapByUserFeaters Позвольте мне установить экстент карты либо по заданному экстенту, либо по функциям пользователя. .

  • setInteractionForPlotBoundriesLayer добавляет взаимодействие для PlotBoundriesLayer.

  • setHoverInteractionForUserPlotBoundries добавляет всплывающее взаимодействие для plotUserBoundriesLayer.

  • clear allSense выбранные функции. Так что они больше не будут выделяться.

  • setBackgroundTileLayer позвольте мне показать конкретный c фон TileLayer.

  • togglePlotBoundriesLayers давайте спрячем или покажите векторный слой, который затем отображается.

import Map from "ol/Map";
import TileWMS from "ol/source/TileWMS";
import TileLayer from "ol/layer/Tile";
import View from "ol/View";
import OSM from "ol/source/OSM";
import BingMaps from "ol/source/BingMaps";
import VectorSource from "ol/source/Vector";
import { bbox as bboxStrategy } from "ol/loadingstrategy";
import GeoJSON from "ol/format/GeoJSON";
import { Vector, Group, Tile } from "ol/layer";
import Select from "ol/interaction/Select";
import { Feature } from "ol";
import { Polygon } from "ol/geom";
import { Fill, Stroke, Style } from "ol/style";

class OlMap {
  constructor() {
    this.createNewMap();
    this.createBackgroundLayerGroups();
  }

  createNewMap() {
    this.map = this.createMap();
  }

  createMap() {
    return new Map({
      target: null,
      layers: [],
      view: new View({
        center: [594668.0262129545, 6602083.305674396],
        maxZoom: 19,
        zoom: 14,
      }),
    });
  }

  createBackgroundLayerGroups() {
    this.layersOSM = new Group({
      layers: [
        new Tile({
          source: new OSM(),
        }),
        new Tile({
          source: new BingMaps({
            imagerySet: "Aerial",
            key: process.env.REACT_APP_BING_MAPS,
          }),
          visible: false,
        }),
      ],
    });
  }

  clearAllBoundriesLayers() {
    this.map.getLayers().forEach((layer) => {
      if (
        layer.get("name") === "plotBoundriesLayer" ||
        layer.get("name") === "plotUserBoundriesLayer"
      ) {
        layer.getSource().clear();
        this.map.removeLayer(layer);
      }
    });
    if (this.select) {
      this.select.getFeatures().clear();
    }
  }

  addBoundriesLayer(featureSelected) {
    this.clearAllBoundriesLayers();
    let vectorSource = new VectorSource({
      format: new GeoJSON(),
      minScale: 15000000,
      loader: function (extent, resolution, projection) {
        /*
          Link for the DLV
          let url = process.env.REACT_APP_MAP_API +
            extent.join(",") +
            ",EPSG:3857";
          */ let url = process.env.REACT_APP_MAP_API +
          extent.join(",") +
          ",EPSG:3857";
        // */
        let xhr = new XMLHttpRequest();
        xhr.open("GET", url);
        let onError = function () {
          vectorSource.removeLoadedExtent(extent);
        };
        xhr.onerror = onError;
        xhr.onload = function () {
          if (xhr.status === 200) {
            let features = vectorSource
              .getFormat()
              .readFeatures(xhr.responseText);
            features.forEach(function (feature) {
              //ID for the DLV
              //feature.setId(feature.get("OBJ_ID"));
              feature.setId(feature.get("OIDN"));
            });
            vectorSource.addFeatures(features);
          } else {
            onError();
          }
        };
        xhr.send();
      },
      strategy: bboxStrategy,
    });
    let vector = new Vector({
      //minZoom: 13,
      source: vectorSource,
    });
    this.setInteractionForPlotBoundriesLayer(vector, featureSelected);
    vector.set("name", "plotBoundriesLayer");
    this.map.addLayer(vector);
  }

  addUsersPlotBoundriesLayer(featureSelected, featureHovered, newFeatures) {
    this.clearAllBoundriesLayers();
    if (newFeatures.length > 0) {
      let vectorSource = new VectorSource({
        format: new GeoJSON(),
        minScale: 15000000,
        strategy: bboxStrategy,
      });
      newFeatures.forEach((newFeature) => {
        let feature = new Feature({
          geometry: new Polygon([newFeature.geometry]),
        });
        feature.setId(newFeature.plotId);
        vectorSource.addFeature(feature);
      });
      let vector = new Vector({
        //minZoom: 13,
        source: vectorSource,
      });
      this.setInteractionForPlotBoundriesLayer(vector, featureSelected);
      this.setHoverInteractionForUserPlotBoundries(vector, featureHovered);
      vector.set("name", "plotUserBoundriesLayer");
      this.plotsExtent = vectorSource.getExtent();
      this.map.addLayer(vector);
    }
  }

  setExtentOfMapByUserFeaters(extent) {
    if (extent === undefined) {
      if (this.plotsExtent !== undefined && this.plotsExtent[0] !== Infinity) {
        this.map.getView().fit(this.plotsExtent);
      }
    } else {
      this.map.getView().fit(extent);
    }
  }

  setInteractionForPlotBoundriesLayer(layer, featureSelected) {
    this.select = new Select({
      layers: [layer],
    });
    this.select.on("select", (event) => featureSelected(event, this.select));
    this.map.addInteraction(this.select);
  }

  setHoverInteractionForUserPlotBoundries(layer, featureHovered) {
    this.hoveredFeature = null;
    let defaultStyle = new Style({
      stroke: new Stroke({
        width: 2,
        color: "#9c1616",
      }),
      fill: new Fill({ color: "#c04e4e" }),
    });
    let hoveredStyle = new Style({
      stroke: new Stroke({
        width: 2,
        color: "#9c1616",
      }),
      fill: new Fill({ color: "#9c1616" }),
    });
    this.map.on("pointermove", (e) => {
      layer
        .getSource()
        .getFeatures()
        .forEach((feature) => {
          feature.setStyle(defaultStyle);
        });
      let newFeature = null;
      this.map.forEachFeatureAtPixel(e.pixel, (f) => {
        newFeature = f;

        newFeature.setStyle(hoveredStyle);
        return true;
      });

      if (newFeature) {
        if (
          this.hoveredFeature === null ||
          this.hoveredFeature !== newFeature
        ) {
          this.hoveredFeature = newFeature;
          featureHovered(this.hoveredFeature.id_);
        }
      } else {
        if (this.hoveredFeature !== null) {
          this.hoveredFeature = null;
          featureHovered(null);
        }
      }
    });
  }

  hoveredSideBarFeatureHandler(hoveredFeatureId) {
    let defaultStyle = new Style({
      stroke: new Stroke({
        width: 2,
        color: "#9c1616",
      }),
      fill: new Fill({ color: "#c04e4e" }),
    });
    let hoveredStyle = new Style({
      stroke: new Stroke({
        width: 2,
        color: "#9c1616",
      }),
      fill: new Fill({ color: "#9c1616" }),
    });
    this.map.getLayers().forEach((layer) => {
      if (layer.get("name") === "plotUserBoundriesLayer") {
        layer
          .getSource()
          .getFeatures()
          .forEach((feature) => {
            if (feature.id_ === hoveredFeatureId) {
              feature.setStyle(hoveredStyle);
            } else {
              feature.setStyle(defaultStyle);
            }
          });
      }
    });
  }

  clearSelect() {
    this.select.getFeatures().clear();
  }

  setBackgroundTileLayer(type) {
    if (this.backgroundTileType === null) {
      this.backgroundTileType = "OPENSTREETMAP";
    }

    if (this.map.getLayers().getArray().length === 0) {
      this.map.setLayerGroup(this.layersOSM);
    } else {
      if (this.backgroundTileType !== type) {
        this.backgroundTileType = type;
        console.log(this.map.getLayers());
        this.map.getLayers().getArray()[0].setVisible(false);
        this.map.getLayers().getArray()[1].setVisible(false);
        if (type === "OPENSTREETMAP") {
          this.map.getLayers().getArray()[0].setVisible(true);
        } else if (type === "BING MAPS") {
          this.map.getLayers().getArray()[1].setVisible(true);
        }
      }
    }
  }

  togglePlotBoundriesLayers(state) {
    if (this.plotBoundriesState === null) {
      this.plotBoundriesState = true;
    }
    if (this.plotBoundriesState !== state) {
      this.plotBoundriesState = state;
      this.map.getLayers().forEach((layer) => {
        if (layer.get("name") === "plotBoundriesLayer") {
          layer.setVisible(state);
        }
        if (layer.get("name") === "plotUserBoundriesLayer") {
          console.log(state);
          layer.setVisible(state);
        }
      });
    }
  }

  addTileLayer(url) {
    const wmsLayer = new TileLayer({
      source: new TileWMS({
        url,
        params: {
          TILED: true,
        },
        crossOrigin: "Anonymous",
      }),
    });
    this.map.addLayer(wmsLayer);
  }
}

export default new OlMap();

На данный момент мне интересно, хорошо ли я работаю или что можно сделать по-другому или лучше оптимизировать мой код , Это поможет мне в конечном итоге не застрять с кодом, который я создал в начале проекта.

Большое спасибо заранее!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...