W
W
Wasya UK2020-08-19 15:22:36
typescript
Wasya UK, 2020-08-19 15:22:36

Why are all the markers outside the mapbox?

I'm trying to deal with mapbox + typescript. Everything was great until I tried the marker example. I didn’t change anything in the logic, I just remade it for typescript. For some reason, the block with markers ignores the size of the map itself and stretches as much as it wants. I set the width both through css and through the attribute, it did not help. What could be the problem?
5f3d192d6b50b264568559.png

I am attaching the code
import React, { Component } from "react";

import ENVIRONMENT from "../../environment";

import "./map-style.css";

import mapboxgl from "mapbox-gl";

import mapboxDefaultLayers from "./map-layers";
import mapboxDefaultSources from "./map-sources";

mapboxgl.accessToken = ENVIRONMENT.mapbox.accessToken;

export interface MapControllerProperties {
  map: mapboxgl.Map
}

export interface MapProperties extends mapboxgl.MapboxOptions {
  onload(map: mapboxgl.Map): void
}

export interface PaintParameters {
  'fill-extrusion-color': string,
  'fill-extrusion-height': []
}

export interface LayerParameters {
  id: string,
  source: string,
  'sourse-layer': string,
  fiter: Array<string>,
  type: string,
  minzoom: number,
  paint: PaintParameters
}

export const defaultMapProperties: MapProperties = {
  style: "mapbox://styles/mapbox/streets-v11",
  container: "map",
  zoom: 0,
  center: [0, 0],
  onload: () => { console.log("Map was succesfully loaded..."); }
};

export class MapController implements MapControllerProperties {
  map: mapboxgl.Map;

  constructor(props: MapProperties = defaultMapProperties) {
    this.map = new mapboxgl.Map(props);
    this.map.addControl(new mapboxgl.NavigationControl());

    this.map.on('load', () => {
      try {
        props.onload(this.map);
      } catch (err) {
        console.error(err);
      }
    });
  }

  public static createLayer(map: mapboxgl.Map, layerParameters: mapboxgl.Layer, labelLayerId?: string) {
    try {
      map.addLayer(layerParameters, labelLayerId);
    } catch (err) {
      console.error(err);
    }
  }

  public static createSource(map: mapboxgl.Map, sourceParameters: mapboxgl.Source | any, labelSourceId: string) {
    try {
      map.addSource(labelSourceId, sourceParameters);
    } catch (err) {
      console.error(err);
    }
  }

  public createLayer(layerParameters: mapboxgl.Layer, labelLayerId?: string) {
    MapController.createLayer(this.map, layerParameters, labelLayerId);
  }

  public createSource(sourceParameters: mapboxgl.Source | any, labelSourceId: string) {
    MapController.createSource(this.map, sourceParameters, labelSourceId);
  }
}

export default class Map extends Component<any, any> {
  mapController?: MapController;

  markers: any = {};
  markersOnScreen: any = {};

  constructor(props: any) {
    super(props);
  }

  componentDidMount() {
    this.mapController = new MapController({
      style: "mapbox://styles/mapbox/streets-v11",
      container: "map",
      onload: () => {
        if (!this.mapController) { return; }

        this.mapController.createLayer(mapboxDefaultLayers.Buildings3D);
        this.mapController.createSource(mapboxDefaultSources.Earthquakes, 'earthquakes');
        this.mapController.createLayer(mapboxDefaultLayers.ClusterCircle);
        this.mapController.createLayer(mapboxDefaultLayers.ClusterCircleLabel);

        // after the GeoJSON data is loaded, update markers on the screen and do so on every map move/moveend
        this.mapController.map.on('data', (e) => {
            if (!this.mapController) { return; }
            if (e.sourceId !== 'earthquakes' || !e.isSourceLoaded) return;

            this.mapController.map.on('move', this.updateMarkers);
            this.mapController.map.on('moveend', this.updateMarkers);
            this.updateMarkers();
        });
      }
    });
  }

  private updateMarkers = () => {
    if (!this.mapController) { return; }

    let newMarkers = {};
    let features: any = this.mapController.map.querySourceFeatures('earthquakes');

    // for every cluster on the screen, create an HTML marker for it (if we didn't yet),
    // and add it to the map if it's not there already
    for (let i = 0; i < features.length; i++) {
      let coords = features[i].geometry.coordinates;
      let props = features[i].properties;

      if (!props.cluster) continue;

      let id = props.cluster_id;
      let marker = this.markers[id];

      if (!marker) {
        let el: any = this.createDonutChart(props);
        marker = this.markers[id] = new mapboxgl.Marker({ element: el }).setLngLat(coords);
      }

      newMarkers[id] = marker;

      if (!this.markersOnScreen[id]) marker.addTo(this.mapController.map);
    }

    // for every marker we've added previously, remove those that are no longer visible
    for (let id in this.markersOnScreen) {
      if (!newMarkers[id]) this.markersOnScreen[id].remove();
    }

    this.markersOnScreen = newMarkers;
  }

  // code for creating an SVG donut chart from feature properties
  private createDonutChart = (props) => {
    let offsets: any = [];
    let counts = [
      props.mag1,
      props.mag2,
      props.mag3,
      props.mag4,
      props.mag5
    ];
    let total = 0;
    let fontSize = total >= 1000 ? 22 : total >= 100 ? 20 : total >= 10 ? 18 : 16;
    let r = total >= 1000 ? 50 : total >= 100 ? 32 : total >= 10 ? 24 : 18;
    let r0 = Math.round(r * 0.6);
    let w = r * 2;
    let html = '<div><svg width="' + w + '" height="' + w + '" viewbox="0 0 ' + w + ' ' + w + '" text-anchor="middle" style="font: ' + fontSize + 'px sans-serif; display: block">';
    const colors = ['#fed976', '#feb24c', '#fd8d3c', '#fc4e2a', '#e31a1c'];

    for (let i = 0; i < counts.length; i++) {
      offsets.push(total);
      total += counts[i];
    }

    for (let i = 0; i < counts.length; i++) {
      html += this.donutSegment(
        offsets[i] / total,
        (offsets[i] + counts[i]) / total,
        r,
        r0,
        colors[i]
      );
    }

    html += '<circle cx="' + r + '" cy="' + r + '" r="' + r0 + '" fill="white" /><text dominant-baseline="central" transform="translate(' + r + ', ' + r + ')">' + total.toLocaleString() + '</text></svg></div>';

    let el = document.createElement('div');
    el.innerHTML = html;
    return el.firstChild;
  }

  private donutSegment = (start, end, r, r0, color) => {
    if (end - start === 1) end -= 0.00001;

    const a0 = 2 * Math.PI * (start - 0.25);
    const a1 = 2 * Math.PI * (end - 0.25);

    const x0 = Math.cos(a0), y0 = Math.sin(a0);
    const x1 = Math.cos(a1), y1 = Math.sin(a1);

    const largeArc = end - start > 0.5 ? 1 : 0;

    return [
      '<path d="M',
      r + r0 * x0,
      r + r0 * y0,
      'L',
      r + r * x0,
      r + r * y0,
      'A',
      r,
      r,
      0,
      largeArc,
      1,
      r + r * x1,
      r + r * y1,
      'L',
      r + r0 * x1,
      r + r0 * y1,
      'A',
      r0,
      r0,
      0,
      largeArc,
      0,
      r + r0 * x0,
      r + r0 * y0,
      '" fill="' + color + '" />'
    ].join(' ');
  }

  render() {
    return (
      <div id="map" width="1200px" height="800px" style={{ width: '1200px', height: '800px' }}></div>
    );
  }
}

Answer the question

In order to leave comments, you need to log in

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question