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)'
}
}