fix in adding and editing markers
[u/erikhf/frm.git] / src / components / map / map.ts
1 import {Component, EventEmitter,CORE_DIRECTIVES,} from 'angular2/angular2';
2 import {Headers, Http} from 'angular2/http';
3
4 @Component({
5     selector: 'mou-map',
6     directives: [CORE_DIRECTIVES],
7     events: ['newactive', 'neworg'],
8     templateUrl: './components/map/map.html'
9 })
10
11
12 export class Map {
13
14     hideModal:any;
15     map:Object;
16     http:Http;
17     LEVEL:number;
18     allLevels:Object;
19     runned:boolean;
20     uprunned:boolean;
21     parent:Object;
22     activeId:string;
23     currentPos:Object;
24     currentMarker:Object;
25     isSearched:boolean;
26     popupON:boolean;
27     popup:Object;
28     COLORS:Object;
29     colornum:number;
30
31     /**
32      * initializes all the global variabels
33      * @param http - for http requests
34      */
35     constructor(http:Http) {
36
37         this.activeId = null;
38         this.newactive = new EventEmitter();
39         this.neworg = new EventEmitter();
40         this.map = new google.maps.Map(document.getElementById("map"), {
41             center: {lat: 0, lng: 0},
42             zoom: 12,
43             mapTypeControlOptions: {
44                 position: google.maps.ControlPosition.BOTTOM_CENTER
45             },
46             zoomControlOptions: {
47                 position: google.maps.ControlPosition.LEFT_BOTTOM
48             },
49             streetViewControl: false
50         });
51         this.init();
52         this.http = http;
53         this.LEVEL = 2;//
54         this.runned = false;
55         this.getLevels(this);
56         this.parent = null;
57         this.currentPos = null;
58         this.uprunned = false;
59         this.currentMarker = null;
60         this.isSearched = false;
61         this.colornum = 0;
62         this.COLORS = ['#ede1bb', '#1d407e', '#ff512e', '#662d47', '#3b3a35', '#419175', '#983e41', '#f3002d', '#b0a875', '#00bfb5', '#926851', '#47a0a4', '#333f50', '#6f007b'];
63         this.popupON = false;
64         this.popup = null;
65     }
66
67
68     /**
69      * Sets the global variabel
70      * @param id - id of the active marker
71      */
72     setActiveId(id) {
73         this.activeId = id;
74     }
75
76     /**
77      * returns the global map
78      * @returns {Object}
79      */
80     getMap() {
81         return this.map;
82     }
83
84     /**
85      * returns global http
86      * @returns {Http}
87      */
88     getHttp() {
89         return this.http;
90     }
91
92     /**
93      * Sets the avctive markers position
94      * @param latlng - position of the active marker
95      */
96     setcurrentPos(latlng) {
97         this.currentPos = latlng;
98     }
99
100     /**
101      * returns the active markers position
102      * @returns {Object}
103      */
104     getcurrentPos() {
105         return this.currentPos;
106     }
107
108     /**
109      * sets the parent of the avtive marker
110      * @param id - of the parent
111      */
112     setParent(id) {
113         this.parent = id;
114     }
115
116     /**
117      * returns the actice markers parent
118      * @returns {Object}
119      */
120     getParent() {
121         return this.parent;
122     }
123
124     /**
125      * sets a bool value for if the addListner for drilling down has runned (little hack)
126      * @param value - for the runned variabel
127      */
128     setRunned(value) {
129         this.runned = value;
130     }
131
132     /**
133      * sets a bool value for if the addListner for drilling up has runned (little hack)
134      * @param value - for the upRunned variabel
135      */
136     setupRunned(value) {
137         this.uprunned = value;
138     }
139
140     /**
141      * sets the current level in the org.unit hierarchy
142      * @param value - for the level variabel
143      */
144     setLevel(value) {
145         this.LEVEL = value;
146     }
147
148     /**
149      * add level when drilling down (little hack for synconisity)
150      */
151     addLevel() {
152         this.LEVEL++;
153     }
154
155     /**
156      * goes up level when drilling up (little hack for synconisity)
157      */
158     upLevel() {
159         this.LEVEL--;
160     }
161
162     /**
163      * initiates the map with position and zoom
164      */
165     init() {
166
167         let map = this.map;
168         let pos = {lat: 9.1, lng: -11.6};
169         map.setCenter(pos, 0);
170         map.setZoom(7);
171
172     }
173
174     /**
175      * prints out error messages in the console
176      * @param error - the error massage
177      */
178     logError(error) {
179         console.error(error);
180
181     }
182
183     /**
184      * gets data from DHIS API
185      * @param query - for what kind of data to retrieve
186      * @param instance - this instance to use
187      * @param isParent - little hack to see if you want to levels up (the parent of a parent)
188      */
189     getData(query, instance, isParent) {
190         instance.http.get(dhisAPI + '/api/organisationUnits' + query)
191             .map(res => res.json())
192             .subscribe(
193                 res => instance.parseResult(res, instance, isParent),
194                 error => instance.logError(error)
195             );
196     }
197
198     /**
199      * Gets the number of levels in the org.unit hierarchy from DHIS
200      */
201     getLevels() {
202         this.http.get(dhisAPI + '/api/organisationUnitLevels')
203             .map(res => res.json())
204             .subscribe(
205                 res => this.saveLevelTotalandGetdata(res, this),
206                 err => this.logError(err)
207             );
208     }
209
210     /**
211      * Saves the data from getLevels() in a global variabel and gets all the data from the second level.
212      * @param res - result from getLevels()
213      * @param instance - witch scope we are in
214      */
215     saveLevelTotalandGetdata(res, instance) {
216         instance.allLevels = res.pager.total;
217         instance.getData('?paging=false&level=2', instance, false);
218     }
219
220     /**
221      * parses all the data from getData() and calles methods based on the incomming data.
222      * @param res - result from getData()
223      * @param instance - witch scope we are in
224      * @param isParent - if it is a parent we have asked for
225      */
226     parseResult(res, instance, isParent) {
227         if (isParent) {
228             instance.setParent(res.parent.id);
229             instance.getData('/' + res.parent.id + '/children', instance, false);
230         }
231         else {
232             if (res.organisationUnits) {
233                 for (let item in res.organisationUnits) {
234                     this.getData('/' + res.organisationUnits[item].id, this);
235
236                 }
237                 instance.setupRunned(false);
238                 instance.setRunned(false);
239             } else if (!res.displayName && res.children) {
240                 for (let item in res.children) {
241                     if (res.children[item].level == instance.LEVEL) {
242                         this.getData('/' + res.children[item].id, this);
243                     }
244                 }
245                 instance.setRunned(false);
246                 instance.setupRunned(false);
247             }
248             else {
249                 this.drawPolygon(res, instance);
250             }
251         }
252     }
253
254     /**
255      * creates and draws up the geojson polygons and adds listeners to them.
256      * @param item - an org.unit object
257      * @param instance - witch scope we are in
258      */
259     drawPolygon(item, instance) {
260         let feature;
261         let incoming:string;
262         incoming = item.featureType.toLowerCase();
263         switch (incoming) {
264             case "point":
265                 feature = 'Point';
266                 break;
267             case "multi_polygon":
268                 feature = 'MultiPolygon';
269                 break;
270             case "polygon":
271                 feature = 'MultiPolygon';
272                 break;
273             default:
274         }
275
276         if (feature !== undefined) {
277             let unit = {
278                 "type": "Feature",
279                 "geometry": {
280                     "type": feature,
281                     "coordinates": JSON.parse(item.coordinates)
282                 },
283                 "properties": {
284                     "title": item.name,
285                     "name": item.name,
286                     "id": item.id,
287                     "color": instance.COLORS[instance.colornum],
288                     "icon": null
289                 }
290             };
291             if (instance.COLORS.length == instance.colornum) {
292                 instance.colornum = 0;
293             } else {
294                 instance.colornum++;
295             }
296
297             if (unit.geometry.type == 'Point') {
298                 unit.properties.icon = {
299                     path: google.maps.SymbolPath.CIRCLE,
300                     strokeColor: 'black',
301                     scale: 4
302                 };
303                 instance.map.setCenter({lat: unit.geometry.coordinates[1], lng: unit.geometry.coordinates[0]});
304             }
305
306             this.map.data.addGeoJson(unit);
307             this.map.data.setStyle(function (feature) {
308                 let color = 'gray';
309                 let icon;
310                 if (feature.getProperty('icon') !== null) {
311                     icon = feature.getProperty('icon');
312                 }
313                 color = feature.getProperty('color');
314                 return /** @type {google.maps.Data.StyleOptions} */({
315                     fillColor: color,
316                     fillOpacity: 0.91,
317                     strokeColor: 'white',
318                     strokeWeight: 2,
319                     icon: icon
320                 });
321             });
322             if (instance.isSearched) {
323                 instance.seeDetails();
324             }
325             this.map.data.addListener('click', function (event) {
326                 $('#myModal').modal('show');
327                 instance.setActiveId(event.feature.O.id);
328                 instance.setcurrentPos(event.latLng);
329
330                 if (instance.uprunned == false && instance.LEVEL == 2) {
331                     this.hideModal = document.getElementById("topLevel").style.display = "block";
332                     this.hideModal = document.getElementById("middleLevel").style.display = "none";
333                     this.hideModal = document.getElementById("bottomLevel").style.display = "none";
334                 }
335                 else if (instance.runned == false && instance.LEVEL < instance.allLevels) {
336                     this.hideModal = document.getElementById("topLevel").style.display = "none";
337                     this.hideModal = document.getElementById("middleLevel").style.display = "block";
338                     this.hideModal = document.getElementById("bottomLevel").style.display = "none";
339                 } else if (instance.runned == false && instance.LEVEL <= instance.allLevels) {
340                     this.hideModal = document.getElementById("topLevel").style.display = "none";
341                     this.hideModal = document.getElementById("middleLevel").style.display = "none";
342                     this.hideModal = document.getElementById("bottomLevel").style.display = "block";
343
344                     instance.setcurrentPos(event.latLng);
345                 }
346             });
347
348 //slette ?? §§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§
349             /* this.map.data.addListener('mouseover', function (e) {


350              if(!instance.popupON) {
351              instance.popupON = true;
352
353              instance.popup = new google.maps.InfoWindow({
354              content: e.feature.getProperty('name'),
355              position: e.latLng
356              });
357              instance.popup.open(instance.map);
358
359              }
360              });

361              this.map.data.addListener('mouseout', function (event) {


362              instance.popupON = false;
363              instance.popup.open(null);

364              });*/
365
366         }
367     }
368
369     /**
370      * removes the polygon on current level and calles getData on one level down in the org.unit hierarchy
371      */
372     drillDown() {
373         this.closeModal();
374         let map = this.getMap();
375         let id = this.activeId;
376         let level = this.LEVEL;
377         this.setRunned(true);
378         this.setParent(id);
379
380         map.data.forEach(function (feature) {
381             if (!(feature.O.id == id && level == 3)) {
382                 map.data.remove(feature);
383
384             }
385         });
386
387         this.addLevel();
388         this.getData('/' + id + '/children', this);
389
390     }
391
392     /**
393      *removes the plogons on the current level and calles the get data with tha parents id and set parent true. this to say that we want this parent's parent
394      */
395     drillUp() {
396         this.setupRunned(true);
397         this.upLevel();
398         let instance = this;
399         this.closeModal();
400         this.map.data.forEach(function (feature) {
401             instance.map.data.remove(feature);
402
403         });
404         if (this.currentMarker !== null) {
405             this.currentMarker.setMap(null);
406         }
407         let parent = instance.getParent();
408         instance.getData('/' + parent, instance, true);
409
410         this.closeModal();
411     }
412
413     /**
414      * focuses map and colors to the clicked marker/polygon and fires an event to sidebar with the id of the marker
415      */
416     seeDetails() {
417
418         console.log("inne i seeDetails");
419         let map = this.getMap();
420         let id = this.activeId;
421         let instance = this;
422         this.closeModal();
423         map.data.forEach(function (feature) {
424             if (feature.getProperty('id') == id) {
425                 feature.setProperty('color', 'red');
426                 if (feature.getProperty('icon') !== null) {
427                     feature.O.icon.strokeColor = 'red';
428                 }
429                 this.isSearched = false;
430                 console.log(this.isSearched);
431             }
432             else {
433                 feature.setProperty('color', 'gray');
434                 if (feature.getProperty('icon') !== null) {
435                     feature.O.icon.strokeColor = 'black';
436                 }
437             }
438         });
439         this.newactive.next(this.activeId);
440     }
441
442     /**
443      * gets the position of the clicked position on the map, saves the parent and sends it in an event.
444      */
445     addUnit() {
446         this.closeModal();
447         let pos = this.getcurrentPos();
448         let lat = pos.lat();
449         let lng = pos.lng();
450         let parent = this.getParent();
451
452         let location = {lat: lat, lng: lng};
453         let event = {location, parent};
454         this.neworg.next(event);
455         this.closeModal();
456         this.setRunned(false);
457     }
458
459     /**
460      * triggered from an event in search and gets the search object from the DHIS API
461      * then calles mapupdate()
462      * @param event - event from an emitter
463      */
464     update(event) {
465         if(event !== null) {
466             if (this.currentMarker) {
467                 this.currentMarker.setMap(null);
468             }
469
470             this.newactive.next(event);
471             let map = this.getMap();
472             let http = this.getHttp();
473
474             map.data.forEach(function (feature) {
475                 map.data.remove(feature);
476             });
477             http.get(dhisAPI + '/api/organisationUnits/' + event)
478                 .map(res => res.json())
479                 .subscribe(
480                     res => this.mapUpdate(res, this)
481                 );
482         }
483     }
484
485     /**
486      * updates varabels activeId, level and parent to matche the incomming object and gets all the children on the same level.
487      * Then it calles drawPolygon()
488      * @param res - org.unit object
489      * @param instance
490      */
491     mapUpdate(res, instance) {
492         this.setLevel(res.level);
493         this.setActiveId(res.id);
494         this.isSearched = true;
495         this.setParent(res.parent.id);
496        // this.setcurrentPos({lat: JSON.parse(res.coordinates)[1],lng: JSON.parse(res.coordinates)[0]});
497
498         instance.getData('/' + res.parent.id + '/children', instance);
499         if (res.coordinates == null || instance.LEVEL == instance.allLevels) {
500             instance.http.get(dhisAPI + '/api/organisationUnits/' + res.parent.id)
501                 .map(res => res.json())
502                 .subscribe(
503                     res => instance.drawPolygon(res, instance)
504                 );
505         }
506
507     }
508
509     /**
510      * adds a temperary marker so the user can see an update of the latitude and longitude of a marker
511      * @param pos - position for the temp marker
512      */
513     tempMarker(pos) {
514         if (this.currentMarker)
515             this.currentMarker.setMap(null);
516         if(pos != null) {
517             let current = {};
518             current.lat = Math.round(this.getcurrentPos().lat() * 10000) / 10000;
519             current.lng = Math.round(this.getcurrentPos().lng() * 10000) / 10000;
520
521             let position = {};
522             position.lat = Math.round(pos.lat * 10000) / 10000;
523             position.lng = Math.round(pos.lng * 10000) / 10000;
524
525             if ((current.lat != position.lat) || (current.lng != position.lng)) {
526
527                 let map = this.map;
528
529
530                 this.currentMarker = new google.maps.Marker({
531                     position: pos,
532                     map: map,
533                     title: 'neworg',
534                     icon: {
535                         path: google.maps.SymbolPath.CIRCLE,
536                         strokeColor: "#871F78",
537                         scale: 4
538                     }
539                 });
540                 this.currentMarker.setMap(map);
541                 map.panTo(this.currentMarker.getPosition());
542             }
543         }
544
545     }
546
547
548     /**
549      * closes the modal box over the map.
550      */
551     closeModal() {
552         $("#myModal").modal("hide");
553         this.setRunned(false);
554     }
555
556 }
557
558
559
560
561