import { Component, ElementRef, Input, Output, EventEmitter, AfterViewInit, OnChanges } from '@angular/core'; import { BaseMapClass, MapDisplayTypes, IMapItem } from './baseMap'; import { RdLib } from '../../base/rdLib'; declare const ymaps: any; declare const jQuery; @Component({ selector: 'rd-ymap', template: `
` }) export class YMap extends BaseMapClass implements OnChanges, AfterViewInit { constructor(elem: ElementRef) { super(elem); this.element = elem; } @Input("rd-change-gradient") changeGradient: boolean = false; @Input("rd-has-balloon") hasBalloon: boolean = false; @Input("rd-has-hint") hasHint: boolean = false; @Input("rd-balloon-content") balloonContent: any; @Input("rd-hint-content") hintContent: any; @Input("rd-custom-button-text") customButtonText: string; @Input("rd-fullScreen-control") fullScreenControl: boolean = true; @Input("rd-route-editor") routeEditor: boolean = false; @Input("rd-input-search-placeholder") inputSearchPlaceholder: string; @Input("rd-filter-avaible") filterAvaible: boolean = false; @Input("rd-pollygon-editor") pollygonEditor: boolean = false; @Output("rd-click") clickEvent: EventEmitter = new EventEmitter(); @Output("rd-on-hover") onHover: EventEmitter = new EventEmitter(); @Output("rd-custom-button-click") customButtonClick: EventEmitter = new EventEmitter(); @Output("rd-pollygon-event") pollygonEvent: EventEmitter = new EventEmitter(); @Output("rd-on-fullScreen") onfullScreen: EventEmitter = new EventEmitter(); private element; private map; private cluster; private heatmap; private center = [39, 34]; private container; private previousWidth; private placemarkInstance; private geoObjects = []; private clusterGrupGeoObjects = []; private currentZoomLevel = 6; private objectManager; private pollygon; public typeChanges = false; ngAfterViewInit() { this.container = jQuery(this.element.nativeElement).find("#ymap"); } ngDoCheck() { if (this.container && this.container[0].clientWidth && this.previousWidth != this.container[0].clientWidth) { this.previousWidth = this.container[0].clientWidth; if (this.map) this.map.container.fitToViewport(); } } ngOnChanges(changes) { if (!this.container) return; if (changes.type || changes.data) { if (this.map) { if (changes.type) this.typeChanges = true; this.currentZoomLevel = this.map.getZoom(); this.center = this.map.getCenter(); this.map.destroy(); } this.ymapsReady(); } if (changes.changeGradient) { this.heatmap.options.set('gradient', this.changeGradient ? this.customGradient : this.defaultGradient); } if (changes.balloonContent && this.placemarkInstance) { this.placemarkInstance.properties.set("balloonContent", this.balloonContent); } if (changes.hintContent && this.placemarkInstance) { this.placemarkInstance.properties.set("hintContent", this.hintContent); } if (changes.height) { jQuery(this.container).css("height", this.height); } if (changes.pollygonEditor) { if (this.pollygonEditor) this.setPollygonEditor(); else this.map.geoObjects.remove(this.pollygon); } } ymapsReady() { this.center = this.typeChanges ? this.center : this.setCenter(this.data, "ymap"); ymaps.ready(() => { this.setMap(); this.setMapDisplay(this.type); if (this.customButtonText) this.addCustomButton(); this.typeChanges = false; }); } setMap() { this.map = new ymaps.Map(this.container[0], { center: this.center, zoom: this.currentZoomLevel, type: 'yandex#map', // map, satellite, hybrid controls: ['zoomControl', 'typeSelector'], autoFitToViewport: "always" }); if (this.routeEditor) this.map.controls.add('routeEditor'); if (this.fullScreenControl) this.map.controls.add(new ymaps.control.FullscreenControl()); this.map.controls.events.add(["fullscreenenter", "fullscreenexit"], (e) => { if (e._sourceEvent.originalEvent.type == "fullscreenenter") this.onfullScreen.emit(true); else this.onfullScreen.emit(false); }); jQuery(this.container).find(".ymaps-2-1-73-copyrights-pane").remove(); } setPointMap(type: MapDisplayTypes) { if (this.filterAvaible) this.setFilter(); else { this.geoObjects = []; for (let item of this.data as Array) { let placemark = new ymaps.Placemark([item.lat, item.lng], this.getPointProperties(), this.getPointOptions(type, item) ); placemark.events.add('click', (e) => { // let target = e.get('target'); // = (placemark) Getting a reference to the object that generated the event (map). this.placemarkInstance = placemark; let coordinates = placemark.geometry.getCoordinates(); this.clickEvent.emit({ lat: coordinates[0], lng: coordinates[1] }); }); // if (this.hasBalloon) { // placemark.events.add("balloonopen", () => { // placemark.properties.set('balloonContent', RdLib.localization.translateEn("Loading data...")); // }); // } if (this.hasHint) { placemark.events.add("hintopen", () => { // placemark.properties.set('hintContent', RdLib.localization.translateEn("Loading data...")); this.placemarkInstance = placemark; let coordinates = placemark.geometry.getCoordinates(); this.onHover.emit({ lat: coordinates[0], lng: coordinates[1] }); }); } if ((>this.data).length > 1) this.geoObjects.push(placemark); else this.map.geoObjects.add(placemark); } if ((>this.data).length > 1) this.setClusterer(); } } getPointProperties() { return { hintContent: ".", // balloonContent: "" } } getPointOptions(pointMapType: MapDisplayTypes, item: IMapItem) { if (pointMapType == MapDisplayTypes.Point) { return { iconLayout: 'default#image', iconImageHref: item.icon.url || 'assets/common/marker.png', iconImageSize: [25, 25], hasHint: this.hasHint, hasBalloon: this.hasBalloon, // iconImageOffset: [-20, -20] } } else if (pointMapType == MapDisplayTypes.ColorFullPoint) { return { preset: 'islands#grayDotIcon', iconColor: this.getIconColorAccordingToWeight(item.weight) } } } getIconColorAccordingToWeight(weight: number) { let rgbColor; let directlyValue = parseInt(((weight * 255) / 100).toFixed(0)); let inverselyValue = (255 - directlyValue); if (weight < 50) rgbColor = "rgb(" + directlyValue + "," + inverselyValue + ",0)"; else if (weight >= 50 && weight < 70) rgbColor = "rgb(" + directlyValue + "," + directlyValue + ",0)"; else rgbColor = "rgb(" + directlyValue + "," + inverselyValue + ",0)"; return rgbColor; } setAreaMap() { let polygonCoords = []; for (let location of this.data as Array) { polygonCoords.push([location.lat, location.lng]); } let polygon = new ymaps.Polygon([polygonCoords], { hintContent: "Polygon" }, { fillColor: '#00FF0088', strokeWidth: 3, opacity: 0.5, strokeStyle: 'shortdash' }); polygon.events.add('click', (e) => { this.clickEvent.emit(null); }); this.map.geoObjects.add(polygon); } setHeatMap() { ymaps.ready(["Heatmap"]).then(() => { let heatMapData = { type: "FeatureCollection", features: [] }; for (let location of this.data as Array) { heatMapData.features.push({ type: "Feature", geometry: { type: 'Point', coordinates: [location.lat, location.lng] }, properties: { weight: location.weight ? location.weight : 1 } }); } ymaps.modules.require(['Heatmap'], function (Heatmap) { if (this.heatmap) this.heatmap.destroy(); this.heatmap = new Heatmap(heatMapData); this.heatmap.setMap(this.map); }.bind(this)); }); } setLineMap() { } /** * RoutingData * * data = [ { lat: "", lng: "", hint: "", balloon: "", route:[ {lat: "", lng: "", color: "", hint: "", balloon: "", saerchText: ""} ] } ] */ setRoutingMap() { let searchData = []; for (let item of this.data as Array) { let routeCoords = [[item.lat, item.lng]]; // team coords set startPoint for (let routePoint of item.route) routeCoords.push([routePoint.lat, routePoint.lng]); let multiRoute = new ymaps.multiRouter.MultiRoute({ referencePoints: routeCoords, params: { results: 2 } }, { boundsAutoApply: true, routeStrokeWidth: 4, routeStrokeColor: "#E63E92" }); multiRoute.model.setParams({ avoidTrafficJams: true }, true); multiRoute.model.events.once("requestsuccess", () => { for (let idx = 0; idx < routeCoords.length; idx++) { let routeItem; let wayPoint = multiRoute.getWayPoints().get(idx); if (idx == 0) { // team wayPoint.options.set({ iconLayout: 'default#image', iconImageHref: 'assets/img/custom/serviceCar.png', iconImageSize: [25, 25], hasHint: true, hasBalloon: true }); } else { // routePoints routeItem = item.route[idx - 1]; searchData.push({ coords: [routeItem.lat, routeItem.lng], text: routeItem.searchText }) wayPoint.options.set({ preset: "islands#" + routeItem.color + 'CircleIcon', iconContentLayout: idx == 0 ? null : ymaps.templateLayoutFactory.createClass('' + idx + ''), }); } ymaps.geoObject.addon.hint.get(wayPoint); ymaps.geoObject.addon.balloon.get(wayPoint); wayPoint.properties.set({ hintContent: idx == 0 ? item.hint : routeItem.hint, balloonContent: idx == 0 ? item.balloon : routeItem.balloon }); } }); this.map.geoObjects.add(multiRoute); } this.setTrafficControl(); this.setSearchProvider(searchData); } setClusterer() { this.cluster = new ymaps.Clusterer({ preset: 'islands#invertedRedClusterIcons', clusterHideIconOnBalloonOpen: false, geoObjectHideIconOnBalloonOpen: false, balloonContentLayoutHeight: 310 }); this.cluster.events.add(['mouseenter', 'mouseleave'], function (e) { var target = e.get('target'), type = e.get('type'); if (typeof target.getGeoObjects != 'undefined') { // An event occurred on the cluster. if (type == 'mouseenter') { target.options.set('preset', 'islands#invertedOrangeClusterIcons'); } else { target.options.set('preset', 'islands#invertedRedClusterIcons'); } } else { // An event took place on the geo object. if (type == 'mouseenter') { target.options.set('preset', 'islands#darkBlueDotIcon'); } else { target.options.set('preset', 'islands#blueDotIcon'); } } }); this.cluster.events.add('balloonopen', function (e) { let clusterPlacemark = e.get('cluster'); if (clusterPlacemark) { this.clusterGrupGeoObjects = clusterPlacemark.getGeoObjects(); this.placemarkInstance = this.clusterGrupGeoObjects[0]; for (let index in this.clusterGrupGeoObjects) { let placemark = this.clusterGrupGeoObjects[index]; placemark.properties.set('clusterCaption', parseInt(index) + 1); } let firstItemCoordinates = this.placemarkInstance.geometry._coordinates; this.clickEvent.emit({ lat: firstItemCoordinates[0], lng: firstItemCoordinates[1] }); } }.bind(this)); jQuery(this.element.nativeElement).on("click", ".ymaps-2-1-73-b-cluster-tabs__menu-item-text", function (e) { let order = parseInt(e.target.innerText) - 1; this.placemarkInstance = this.clusterGrupGeoObjects[order]; let coordinates = this.placemarkInstance.geometry._coordinates; this.clickEvent.emit({ lat: coordinates[0], lng: coordinates[1], order: order }); }.bind(this)); this.cluster.add(this.geoObjects); this.map.geoObjects.add(this.cluster); if ((>this.data).length && !this.typeChanges) { this.map.setBounds(this.cluster.getBounds(), { checkZoomRange: true }); } } setTrafficControl() { let trafficControl = new ymaps.control.TrafficControl({ state: { trafficShown: false }, options: { size: "small" } }); this.map.controls.add(trafficControl, { float: 'right' }); } setSearchProvider(searchData) { let searchControl = new ymaps.control.SearchControl({ options: { provider: new CustomSearchProvider(searchData), noPlacemark: true, resultsPerPage: 5, placeholderContent: this.inputSearchPlaceholder, size: "small" } }); searchControl.events.add('submit', () => { this.currentZoomLevel = this.map.getZoom(); }, this); searchControl.events.add('clear', () => { this.map.setZoom(this.currentZoomLevel); this.setCenter(this.data, "ymap"); }, this); this.map.controls.add(searchControl, { float: 'right' }); function CustomSearchProvider(points) { this.points = points; } CustomSearchProvider.prototype.geocode = function (request, options) { let deferred = new ymaps.vow.defer(), geoObjects = new ymaps.GeoObjectCollection(), offset = options.skip || 0, limit = options.results || 20, points = []; for (let point of this.points) { if (point.text.toLowerCase().indexOf(request.toLowerCase()) != -1) { points.push(point); } } points = points.splice(offset, limit); for (let item of points) { geoObjects.add(new ymaps.Placemark(item.coords, { name: item.text, // description: item.text, balloonContentBody: '

' + item.text + '

', boundedBy: [item.coords, item.coords] })); } deferred.resolve({ geoObjects: geoObjects, metaData: { geocoder: { request: request, found: geoObjects.getLength(), results: limit, skip: offset } } }); return deferred.promise(); }; } createObjectManager() { this.objectManager = new ymaps.ObjectManager({ clusterize: true, gridSize: 64, clusterIconLayout: "default#pieChart" }); this.map.geoObjects.add(this.objectManager); } setListBoxItems() { let filters = []; let filterTexts = {}; let currentID = 0; for (let item of this.data as Array) { this.objectManager.add({ type: "Feature", id: currentID++, geometry: { type: 'Point', coordinates: [item.lat, item.lng] }, properties: { hintContent: item.hint, balloonContent: item.balloon, iconCaption: item.icon.caption }, options: { preset: "islands#" + item.icon.color + "CircleDotIconWithCaption" } }); if (!filterTexts[item.filterText]) { filterTexts[item.filterText] = true; filters.push(new ymaps.control.ListBoxItem({ data: { content: item.filterText }, state: { selected: true } })); } } return filters; } setFilter() { this.createObjectManager(); let listBoxItems = this.setListBoxItems(); let listBoxControl = new ymaps.control.ListBox({ data: { content: RdLib.localization.translateEn("Filter") }, items: listBoxItems, state: { expanded: false, filters: listBoxItems.reduce((filters, filter) => { filters[filter.data.get('content')] = filter.isSelected() return filters; }, {}) } }); this.map.controls.add(listBoxControl); listBoxControl.events.add(['select', 'deselect'], (e) => { let listBoxItem = e.get('target'); let filters = ymaps.util.extend({}, listBoxControl.state.get('filters')); filters[listBoxItem.data.get('content')] = listBoxItem.isSelected(); listBoxControl.state.set('filters', filters); }); let filterMonitor = new ymaps.Monitor(listBoxControl.state); filterMonitor.add('filters', (filters) => { this.objectManager.setFilter(getFilterFunction(filters)); }); function getFilterFunction(categories) { return function (obj) { let content = obj.properties.balloonContent; return categories[content] } } } addCustomButton() { let buttonLayout = new ymaps.control.Button({ data: { content: this.customButtonText, // image: "" }, options: { // size: "small" } }); buttonLayout.events.add("click", (e) => { this.customButtonClick.emit(); }); this.map.controls.add(buttonLayout, { float: "right" }); } setPollygonEditor() { this.pollygon = new ymaps.Polygon([], {}, { editorDrawingCursor: "crosshair", editorMaxPoints: 5, fillColor: '#DB425A', opacity: 0.3, strokeColor: '#0000FF', strokeWidth: 3, strokeStyle: 'shortdash', draggable: true }); this.map.geoObjects.add(this.pollygon); let stateMonitor = new ymaps.Monitor(this.pollygon.editor.state); stateMonitor.add("drawing", function (newValue) { this.pollygon.options.set("strokeColor", newValue ? '#FF0000' : '#0000FF'); }.bind(this)); this.pollygon.editor.startDrawing(); let pollygonCoords; let pollygonEventValid = true; this.pollygon.events.add(['dragend', 'geometrychange'], function (e) { if (e.get("type") == "geometrychange") { pollygonCoords = e.originalEvent.originalEvent.originalEvent.newCoordinates; if (pollygonCoords[0].length == 5 && pollygonEventValid) { this.pollygonEvent.emit(pollygonCoords); pollygonEventValid = false; } } else if (e.get("type") == "dragend") this.pollygonEvent.emit(pollygonCoords); }.bind(this)); } setGeoJson() { } defaultGradient = { 0.1: 'rgba(128, 255, 0, 0.7)', 0.2: 'rgba(255, 255, 0, 0.8)', 0.7: 'rgba(234, 72, 58, 0.9)', 1.0: 'rgba(162, 36, 25, 1)' }; customGradient = { 0.1: 'rgba(0, 255, 255, 1)', 0.2: 'rgba(0, 127, 255, 1)', 0.3: 'rgba(0, 0, 255, 1)', 0.4: 'rgba(0, 0, 191, 1)', 0.5: 'rgba(0, 0, 159, 1)', 0.6: 'rgba(0, 0, 127, 1)', 0.7: 'rgba(0, 63, 255, 1)', 0.8: 'rgba(63, 0, 91, 1)', 0.9: 'rgba(191, 0, 31, 1)', 1.0: 'rgba(255, 0, 0, 1)' } }