Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
99 views
in Technique[技术] by (71.8m points)

javascript - what is the right way to add time-dimension for leaflet map?

Am using leaflet library with angular 8 and I have succesufully plotted heatmap from values ( longitude , latitude , count that i pass ).

What i want to add time-dimension to the current heatmap to have animation when i hit play arrow of the play/pause bar . i have found plenty of ressources showing the result :

enter image description here

The code of mine is :

mapview.component.html

<div leaflet class="map"
 (leafletMapReady)="onMapReady($event)"
 [leafletZoom]="zoom"
 [leafletCenter]="center"
 [leafletLayers]="layers"
 [leafletLayersControl]="layersControl">

mapview.component.ts

  import {
    ChangeDetectorRef,
    Component,
    EventEmitter,
    Input,
    OnChanges,
    OnDestroy,
    OnInit,
    Output,
    SimpleChanges
} from '@angular/core';
import * as L from 'leaflet';
import {latLng, Layer, tileLayer} from 'leaflet';
import 'leaflet.markercluster';
import {LeafletLayersModel} from '../../models/leaflet-layers.model';
import {Subject} from 'rxjs';
import 'leaflet-extra-markers/';
import 'leaflet-timedimension/dist/leaflet.timedimension.min.js'
import * as HeatmapOverlay from 'leaflet-heatmap';
import * as TimeDimension from 'leaflet-timedimension';


@Component({
    selector: 'app-mapview',
    templateUrl: './mapview.component.html',
    styleUrls: ['./mapview.component.scss']
})
export class MapviewComponent implements OnInit, OnChanges, OnDestroy {
    private destroy$: Subject<boolean> = new Subject<boolean>();
    @Input() mapData: any;
    @Input() showLegend: boolean;
    @Input() showTeamLegend: boolean;
    @Output() mapUpdate = new EventEmitter<boolean>();
    @Input() teamData: any;
    @Input() type: string; // to identify from which component the map is called

    labels = [];
    legend: any;
    public map: L.Map;
    timeDimension:true;

    // Open Street Map and Open Cycle Map definitions
    LAYER_OCM = {
        id: 'opencyclemap',
        name: 'Open Cycle Map',
        enabled: false,
        layer: tileLayer('http://{s}.tile.opencyclemap.org/cycle/{z}/{x}/{y}.png', {
            maxZoom: 18,
            minZoom: 1,
            attribution: 'Open Cycle Map'
        })
    };
    LAYER_OSM = {
        id: 'openstreetmap',
        name: 'Open Street Map',
        enabled: true,
        layer: tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
            maxZoom: 18,
            minZoom: 1,
            attribution: 'Open Street Map'
        }),
    };



    // Form model object
    model = new LeafletLayersModel(
        [this.LAYER_OSM, this.LAYER_OCM],
        this.LAYER_OSM.id
        // , [ this.circle, this.polygon, this.square, this.geoJSON ]
    );

    // Values to bind to Leaflet Directive
    layers: Layer[];
    layersControl = {
        baseLayers: {
            'Open Street Map': this.LAYER_OSM.layer,
            /*
                        'Open Cycle Map': this.LAYER_OCM.layer
            */
        },
    };
    center;
    zoom;
    markers = L.markerClusterGroup();

    constructor(
        private cdRef: ChangeDetectorRef,
    ) {
        this.apply();
    }

    ngOnInit() {
    }

    /** On change update the mapData */
    ngOnChanges(changes: SimpleChanges) {
        const mapData = JSON.parse(this.mapData);
        if (mapData.features) {
            this.removeLayers();
            this.displayMapData();
        }
    }

    ngOnDestroy(): void {
        this.destroy$.next(true);
        this.destroy$.unsubscribe();
    }

    /** To apply the map and update the layers */
    apply() {

        this.zoom = 5;
        this.center = latLng(48.856613,
            2.352222);

        // Get the active base layer
        const baseLayer = this.model.baseLayers.find((l: any) => (l.id === this.model.baseLayer));
        // Get all the active overlay layers
        const newLayers = this.model.overlayLayers
            .filter((l: any) => l.enabled)
            .map((l: any) => l.layer);
        newLayers.unshift(baseLayer.layer);

        this.layers = newLayers;
        this.layers = [...this.layers];
    }

    /** Method executed when the map is ready */
    onMapReady(map: L.Map) {
        //L.timeDimension({timeDimension : true}).addTo(map);
        this.map = map;
        setTimeout(() => {
            map.invalidateSize();
        }, 500);
        this.cdRef.detectChanges();
    }

    /** To display the map data */
    private displayMapData() {
        this.markers = L.markerClusterGroup({
            showCoverageOnHover: false,
        });
        const mapDataObj = JSON.parse(this.mapData);
        if (mapDataObj && mapDataObj.features.length) {
            mapDataObj.features.forEach((data) => {
                if (data.geometry && data.geometry.type === 'ExtraMarkers') {
                    this.center = latLng(mapDataObj.features[0].geometry.coordinates[0],
                        mapDataObj.features[0].geometry.coordinates[1]);
                    this.displayExtraMarkers(data);
                } else if (data[0] && data[0].geometry && data[0].geometry.type === 'Polygon') {
                    this.center = latLng(mapDataObj.features[0][0].geometry.coordinates[0][0][1],
                        mapDataObj.features[0][0].geometry.coordinates[0][0][0]);
                    this.displayZone(data);
                } else if (data.geometry && data.geometry.type === 'Polyline') {
                    this.center = latLng(mapDataObj.features[0].geometry.coordinates[0][0],
                        mapDataObj.features[0].geometry.coordinates[0][1]);
                    this.displayPolyline(data);
                } else if (data.geometry && data.geometry.type === 'heatMap') {
                    this.displayHeatMap(data);
                } else if (data.geometry && data.geometry.type === 'animatedHeatMap') {
                    //this.displayAnimatedHeatMap(data);
                    this.displayAnimatedHeatMap1(data);
                } else if (data.geometry && data.geometry.type === 'bubbleMap') {
                    this.center = latLng(mapDataObj.features[0].geometry.coordinates[0],
                        mapDataObj.features[0].geometry.coordinates[1]);
                    this.displayBubble(data);
                }
            });
            this.layers.push(this.markers);
        }
    }

    /** To display extra markers */
    private displayExtraMarkers(data) {
        const icon = L.ExtraMarkers.icon(data.properties.options);
        const marker = L.marker([data.geometry.coordinates[0], data.geometry.coordinates[1]], {icon});
        const info = data.properties.info;
        if (info) {
            let popup = '';
            if (info.title) {
                popup += `<div style="text-align: center; bottom: 13px; border-bottom: solid 2px #1c2831; position: relative;">
                <span class="name" style="color: #25aae1; "> ${info.title} </span>
             </div>`;
            }
            marker.bindPopup(popup, {className: 'popupCustom'});
        }
        this.markers.addLayer(marker);
    }

    /** To display a polyline */
    private displayPolyline(data) {
        const marker = L.polyline(data.geometry.coordinates, data.properties.options);
        marker.on('click', () => {
            marker.bringToFront(); // to highlight the itinerary with mouse click
        });

        const info = data.properties.info;
        if (info) {
            let popup = '';
            if (info.label) {
                popup += `<div style="text-align: center; bottom: 13px; border-bottom: solid 2px #1c2831; position: relative;">
            <span class="name" style="color: #25aae1; ">${info.label}</span>
         </div>`;
            }
            marker.bindPopup(popup, {className: 'popupCustom'});
        }
        this.layers.push(marker);
    }

    /** To display a zone */
    private displayZone(zone) {
        zone[0].geometry.type = 'Polygon';
        const info = zone[0].properties.options;
        let zoneToAdd = new L.GeoJSON(zone, {
            style: () => {
                return {
                    fillColor: '#d0cfd1', // to fill zone with color
                    color: '#6b6a6c', // to fill Boundaries
                    fillOpacity: 0.3
                };
            }
        });

        if (info) {
            let popup = '';
            if (info.label) {
                popup += `<div style="text-align: center; bottom: 13px; border-bottom: solid 2px #1c2831; position: relative;">
            <span class="name" style="color: #25aae1; "> ${info.label} </span>
         </div>`;
            }
            zoneToAdd.bindPopup(popup, {className: 'popupCustom'});
        }

        // On zones hover => change style
        zoneToAdd.on('mouseover', function () {
            this.setStyle({
                fillOpacity: 0.6
            });
        });
        zoneToAdd.on('mouseout', function () {
            this.setStyle({
                fillOpacity: 0.3
            });
        });

        this.layers.push(zoneToAdd);
    }

    /** To display a bubble for the bubble maps */
    private displayBubble(data) {
        const marker = L.circleMarker([data.geometry.coordinates[0], data.geometry.coordinates[1]], {
            fillColor: '#03a9f4',
            fillOpacity: 0.6,
            radius: data.geometry.coordinates[2]
        });
        const info = data.properties.info;

        if (info) {
            let popup = '';
            if (info.title) {
                popup += `<div style="text-align: center; bottom: 13px; border-bottom: solid 2px #1c2831; position: relative;">
                <span class="name" style="color: #25aae1; "> ${info.title} </span>
             </div>`;
            }
            marker.bindPopup(popup, {className: 'popupCustom'});
        }
        this.layers.push(marker);
    }

    /** To display the heat map */
    private displayHeatMap(data) {
        let heatmapLayer = new HeatmapOverlay(data.cfg);

        this.layers.push(heatmapLayer);

        heatmapLayer.setData({
            max: 8,
            data: data.rows
        });
        this.center = latLng(data.rows[0][data.cfg.latField],
            data.rows[0][data.cfg.lngField]);
    }


    framesDelay(animatedHeatmapLayer, data, i) {
        return new Promise(resolve => {
            if (data && data.rows.length) {
                setTimeout(() => {
                    resolve(
                        animatedHeatmapLayer.addData(data.rows[i])
                    )
                }, 1000);
            }
        });
    }
    async asyncCall(animatedHeatmapLayer, data, length) {
        let i = 1;
        while (i < length) {
            const result = await this.framesDelay(animatedHeatmapLayer, data, i);
            i++
        }
    }

    /** To display the animated heat map */
    displayAnimatedHeatMap(data) {
        let dates = ['2014-09-30', '2015-11-12', '2015-10-10', '2016-01-01', '2016-02-01'];
        let animatedHeatmapLayer = new HeatmapOverlay(data.cfg);
       // let player = new TimeDimension.Player({}).addTo(animatedHeat

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)
Waitting for answers

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...