source: main/trunk/greenstone3/web/interfaces/default/js/map-scripts.js@ 33323

Last change on this file since 33323 was 33323, checked in by ak19, 5 years ago

Forgot to remove some debugging.

  • Property svn:executable set to *
File size: 41.0 KB
Line 
1var mapEnabled = false; // variable to detect when map-scripts have been included into document_scripts.js and classifier_scripts.js,
2 // in which case this variable will be defined.
3 // It will be furthermore be set to true iff we have gps map data that the map can display, false if map-scripts imported but no map data.
4
5//var newLat, newLng = 0;
6var _docList = {};
7_docList.ids = [];
8
9_docList.getDocByIndex = function(index)
10{
11 return _docList[_docList.ids[index]];
12};
13
14var _nearbyDocListByProximity = [];
15
16var _map;
17var _intervalHandle;
18var _baseURL = document.URL.substring(0, document.URL.indexOf("?") + 1);
19var _retrievedClassifiers = [];
20var _preventLoopingSingleMarker = false;
21var _searchRunning = false;
22var _nearbyDocsByDistance = [];
23var _scrollThroughDefault = true; // TODO: default is true
24
25var LOW_OPACITY = 0.1; // make surrounding docs' opacity 10%
26var _DEBUGGING_ = false;
27
28function initializeMapScripts()
29{
30 //console.log("@@@@ initializeMapScripts()");
31
32 var jsonNodeDiv = $("#jsonNodes");
33 if(jsonNodeDiv.length)
34 {
35 //console.log("@@@ JSON node div html: " + jsonNodeDiv.html());
36 var jsonNodes = eval(jsonNodeDiv.html());
37
38 renderMap();
39 if(jsonNodes && jsonNodes.length > 0)
40 {
41 mapEnabled = true;
42 showMap("initializeMapScripts");
43
44 var resultsArea = $('#resultsArea');
45 if (resultsArea.length > 0){
46 resultsArea.css('min-height','500px');
47 }
48 for(var i = 0; i < jsonNodes.length; i++)
49 {
50 _docList[jsonNodes[i].nodeID] = jsonNodes[i];
51 _docList.ids.push(jsonNodes[i].nodeID);
52 var options = {
53 "mainDoc": true
54 };
55 //console.log("#### " + jsonNodes[i].nodeID + " is a main doc: ");
56 //console.log(jsonNodes[i]);
57 createOverlayItems(jsonNodes[i], options);
58 }
59
60 updateMap();
61 }
62 else
63 {
64 //hiding the map
65 mapEnabled = false;
66 hideMap("initializeMapScripts");
67 //return;
68 }
69 }
70 _docList.loopIndex = 0;
71
72 if(_docList.ids.length > 1)
73 {
74 var startStopCheckbox = $("<input>", {"type": "checkbox", "checked": _scrollThroughDefault, "id": "scrollCheckbox"});
75 startStopCheckbox.click(function()
76 {
77 // http://stackoverflow.com/questions/901712/how-to-check-if-a-checkbox-is-checked-in-jquery
78 // http://stackoverflow.com/questions/5270689/attrchecked-checked-does-not-work
79
80 if($('#scrollCheckbox').is(':checked')) // OR: if(document.getElementById('scrollCheckbox').checked)
81 {
82 if(_intervalHandle == null)
83 {
84 _intervalHandle = setInterval(loopThroughMarkers, 2000);
85 }
86 }
87 else
88 {
89 clearInterval(_intervalHandle);
90 _intervalHandle = null;
91 }
92 });
93
94 var label = $("<span>Scroll through places</span>");
95 var container = $("<div>", {"class": "ui-widget-header ui-corner-all", "style": "clear:right; float:right; padding:0px 5px 3px 0px;"});
96 container.append(startStopCheckbox);
97 container.append(label);
98
99 $(container).insertAfter("#map_canvas");
100
101 if (_scrollThroughDefault) {
102 _intervalHandle = setInterval(loopThroughMarkers, 2000);
103 }
104
105 }
106}
107
108function renderMap()
109{
110 //console.log("@@@@in map-scripts::renderMap()");
111 var myOptions =
112 {
113 zoom: 2,
114 center: new google.maps.LatLng(0, 0),
115 mapTypeId: google.maps.MapTypeId.HYBRID
116 };
117 var $map_canvas = $("#map_canvas");
118
119 if ($map_canvas.length > 0) {
120 //console.log("docList is " + _docList.toString());
121 _map = new google.maps.Map($map_canvas[0], myOptions);
122 //console.log("@@@ created Google _map");
123
124 google.maps.event.addListener(_map, 'bounds_changed', performProximitySearch);
125 }
126}
127
128function performProximitySearch()
129{
130 //console.log("@@@@ skipping proximity search for now");
131 //return;
132
133 _gsDebug("*** START OF performProximitySearch()");
134
135
136 if(typeof mapEnabled === 'undefined') return;
137 if(!mapEnabled){ return; }
138
139 _gsDebug("*** Got past mapEnabled test");
140
141
142 for(var i = 0 ; i < _nearbyDocListByProximity.length; i++) {
143 removeMarkersFromMap(_nearbyDocListByProximity[i]);
144 }
145 _nearbyDocListByProximity = [];
146
147
148 _debugPrintBounds(_map.getBounds(), "@@@ performProximitySearch():");
149
150
151 if(_searchRunning)
152 {
153 _gsInfo("*** performProximitySearch(): already running search => not initiating an additional search");
154 return;
155 }
156
157 _searchRunning = true;
158
159 var bounds = _map.getBounds();
160
161 var neLat = bounds.getNorthEast().lat();
162 var neLng = bounds.getNorthEast().lng();
163 var swLat = bounds.getSouthWest().lat();
164 var swLng = bounds.getSouthWest().lng();
165
166 var latDistance = neLat - swLat;
167 var lngDistance = neLng - swLng;
168
169 _gsDebug("****************START*****************");
170 _gsDebug("latDistance = " + latDistance);
171 _gsDebug("lngDistance = " + lngDistance);
172
173 //Check which increment to use for latitude (i.e. 0.001, 0.01, 0.1 or 1 degree increments)
174 var latDelta;
175 var latPrecision;
176 for(var i = 3; i >= 0; i--)
177 {
178 latDelta = (1 / Math.pow(10, i));
179 if((latDistance / latDelta) <= 5 || latDelta == 1)
180 {
181 latPrecision = i;
182 break;
183 }
184 }
185
186 //Check which increment to use for longitude (i.e. 0.001, 0.01, 0.1 or 1 degree increments)
187 var lngDelta;
188 var lngPrecision;
189 for(var i = 3; i >= 0; i--)
190 {
191 lngDelta = (1 / Math.pow(10, i));
192 // Want the grid superimposed on the map to be 5 or fewer steps,
193 // where delta is the grid size to make it so
194 // and precision is the number of decimal places.
195 if((lngDistance / lngDelta) <= 5 || lngDelta == 1)
196 {
197 lngPrecision = i;
198 break;
199 }
200 }
201
202 if(latDelta == 0.1){latDelta = 1; latPrecision = 0; }
203 if(lngDelta == 0.1){lngDelta = 1; lngPrecision = 0; }
204
205
206
207 _gsDebug("Proximity search: lat precision BEFORE = " + latPrecision);
208 _gsDebug("Proximity search: lng precision BEFORE = " + lngPrecision);
209
210 // Want consistent precision for both lat and lng.
211 // So we choose the most conservative (whichever is more zoomed out) for both
212 if(latPrecision < lngPrecision) {
213 lngPrecision = latPrecision;
214 } else if (lngPrecision < latPrecision) {
215 latPrecision = lngPrecision;
216 }
217
218 _gsDebug("Proximity search: lat precision AFTER = " + latPrecision);
219 _gsDebug("Proximity search: lng precision AFTER = " + lngPrecision);
220 _gsDebug("Proximity search with: latDelta = " + latDelta);
221 _gsDebug("Proximity search with: lngDelta = " + lngDelta);
222
223 var query = "";
224 var iMax = Math.floor(latDistance / latDelta) + 1;
225 var jMax = Math.floor(lngDistance / lngDelta) + 1;
226
227 _gsDebug("Proximity search with: iMax = " + iMax);
228 _gsDebug("Proximity search with: jMax = " + jMax);
229
230 _gsDebug("****************END*****************");
231
232 for(var i = 0; i <= iMax; i++) //for(var i = 0; i <= Math.floor(latDistance / latDelta) + 1; i++)
233 {
234 for(var j = 0; j <= jMax; j++) //for(var j = 0; j <= Math.floor(lngDistance / lngDelta) + 1; j++)
235 {
236 //Some necessary variables
237 var newLat = neLat - (latDelta * i);
238 var newLatString = "" + newLat;
239 var newLatTrunc;
240 if(newLat < 0){newLatTrunc = Math.ceil(newLat);}
241 else{newLatTrunc = Math.floor(newLat);}
242
243 var newLng = neLng - (lngDelta * j);
244 var newLngString = "" + newLng;
245 var newLngTrunc;
246 if(newLng < 0){newLngTrunc = Math.ceil(newLng);}
247 else{newLngTrunc = Math.floor(newLng);}
248
249 //Construct query
250 query += "(";
251 query += "CS:\"" + coordToAbsDirected(newLatTrunc, "lat");
252
253 if(latDelta != 1)
254 {
255 query += newLatString.substring(newLatString.indexOf(".") + 1, newLatString.indexOf(".") + latPrecision + 1);
256 }
257 query += " ";
258 query += coordToAbsDirected(newLngTrunc, "lng");
259 if(lngDelta != 1)
260 {
261 query += newLngString.substring(newLngString.indexOf(".") + 1, newLngString.indexOf(".") + lngPrecision + 1);
262 }
263 query += "\"";
264 query += ")";
265
266 if(i != iMax || j != jMax){ query += "+OR+"; } //if(i != (Math.floor(latDistance / latDelta) + 1) || j != (Math.floor(lngDistance / lngDelta) + 1)){ query += "+OR+"; }
267 }
268 }
269
270 // This works, why not from the double loop above?
271 //query = "(CS:\"" + coordToAbsDirected(newLatTrunc, "lat") + " " + coordToAbsDirected(newLngTrunc, "lng") + "\")";
272 //alert("@@@@in map-scripts::performProximitySearch() - query: " + query);
273
274 //var url = gs.xsltParams.library_name + "?a=q&s=RawQuery&rt=rd&c=" + gs.cgiParams.c + "&s1.rawquery=" + query + "&excerptid=jsonNodes";
275 var url = gs.xsltParams.library_name;
276 var data = "a=q&s=RawQuery&rt=rd&c=" + gs.cgiParams.c + "&s1.rawquery=" + query + "&excerptid=jsonNodes";
277 _gsDebug("*** performProximitySearch(): rawQuery query data = " + query);
278
279 $.ajax({type:"POST", url:url, data:data})
280 .success(function(responseText)
281 {
282 //console.log("*** responseText (first 250) = " + responseText.substring(0,256));
283
284 if(responseText.search("id=\"jsonNodes") != -1)
285 {
286 var startIndex = responseText.indexOf(">");
287 var endIndex = responseText.indexOf("</");
288
289 //console.log("@@@@ performSearch, got response: " + responseText);
290
291 var jsonNodes = eval(responseText.substring(startIndex+1, endIndex));
292 _gsDebug("@@@@ performProximitySearch - Number of matches returned from ajax rawQuery search = " + jsonNodes.length);
293 if(jsonNodes && jsonNodes.length > 0)
294 {
295 for(var i = 0; i < jsonNodes.length; i++)
296 {
297 var doc = jsonNodes[i];
298
299 var found = false;
300 for(var j = 0; j < _docList.ids.length; j++) {
301 if(doc.nodeID == _docList.ids[j]) {
302 found = true;
303 _gsDebug("performProximitySearch(): Found nearby ID " + doc.nodeID + " was already drawn:", jsonNodes[i]);
304 break;
305 }
306 }
307
308 if(!found)
309 {
310 _nearbyDocListByProximity.push(doc);
311 createOverlayItems(doc, {"mainDoc": false});
312 }
313
314 }
315 }
316 }
317 else
318 {
319 console.log("map-scripts::performProximitySearch(): No JSON information received");
320 }
321
322 _searchRunning = false;
323 }).fail(function(responseText, textStatus, errorThrown) // fail() has replaced error(), http://api.jquery.com/jquery.ajax/
324 {
325 console.log("In map-scripts.performProximitySearch(): Got an error in ajax call");
326 _searchRunning = false;
327 });
328}
329
330function coordToAbsDirected(coord, type)
331{
332 var value = "" + coord;
333 if(coord < 0)
334 {
335 value = value.substring(1);
336 if(type == "lat")
337 {
338 value += "S";
339 }
340 else
341 {
342 value += "W";
343 }
344 }
345 else
346 {
347 if(type == "lat")
348 {
349 value += "N";
350 }
351 else
352 {
353 value += "E";
354 }
355 }
356
357 return value;
358}
359
360function facetedMapSearch(facet_request_url) {
361 // ask for just the jsonNodes
362 facet_request_url += "excerptid=jsonNodes";
363
364 $.ajax(facet_request_url).done(function(responseText) {
365 var doc;
366 var nodeID;
367
368 //console.log("Sent off facetedMapSearch request to " + facet_request_url + ", got response: ");
369 //console.log(responseText);
370
371 if(responseText.search("id=\"jsonNodes") != -1) {
372 var startIndex = responseText.indexOf(">");
373 var endIndex = responseText.indexOf("</");
374 var jsonNodes = eval(responseText.substring(startIndex+1, endIndex));
375 //console.log(jsonNodes);
376
377 if(jsonNodes && jsonNodes.length > 0) {
378 //console.log("Faceted search got some nodes. Removing all markers first...");
379
380
381 mapEnabled = true;
382 showMap("initializeMapScripts");
383
384 // Remove all the markers/shapes from map
385 // and empty _docList.ids and the _docList of items
386 for(var j = 0; j < _docList.ids.length; j++) {
387 nodeID = _docList.ids[j];
388 doc = _docList.getDocByIndex(j);
389 removeMarkersFromMap(doc);
390 delete _docList[nodeID];
391 }
392 _docList.ids = [];
393
394 // repopulate _docList and _docList.ids and plot on map
395 for(var i = 0; i < jsonNodes.length; i++)
396 {
397 _docList[jsonNodes[i].nodeID] = jsonNodes[i];
398 _docList.ids.push(jsonNodes[i].nodeID);
399 var options = {
400 "mainDoc": true
401 };
402 createOverlayItems(jsonNodes[i], options);
403 }
404
405 // redo map bounds, which will also performProximitySearch
406 updateMap();
407 }
408 else
409 {
410 //hiding the map
411 mapEnabled = false;
412 hideMap("initializeMapScripts");
413 }
414 }
415 _docList.loopIndex = 0;
416
417 });
418
419}
420
421
422function toggleMapSection(options)
423{
424 var sectionID = options["nodeID"];
425
426 var titleClassifierEl = document.getElementById("title"+sectionID);
427 var jsonNodesStr = titleClassifierEl.getAttribute("data-gps-map-json");
428 //alert("@@@@ got jsonNodesStr |" + jsonNodesStr + "|");
429 var jsonNodes = JSON.parse(jsonNodesStr);
430
431 if(options["expand"]){
432 _gsDebug("expanding classifier - sectionID: " + sectionID);
433
434 if(jsonNodes && jsonNodes.length > 0)
435 {
436 for(var i = 0; i < jsonNodes.length; i++)
437 {
438 var doc = jsonNodes[i];
439 if(_docList[doc.nodeID]) continue; // already in list, don't add again
440
441 _docList[doc.nodeID] = doc;
442 _docList.ids.push(doc.nodeID);
443
444 var options = {
445 "mainDoc": true // TODO: should this be true or false???
446 };
447 createOverlayItems(doc, options);
448 }
449 }
450
451 } else { // closing the bookshelf
452 _gsDebug("closing classifier - sectionID: " + sectionID);
453 if(jsonNodes && jsonNodes.length > 0)
454 {
455 for(var i = 0; i < jsonNodes.length; i++)
456 {
457 // remove the doc from _docList and its id from _docList.ids
458 // and remove its markers/shapes from the map
459 var nodeID = jsonNodes[i].nodeID;
460 var doc = _docList[nodeID];
461 if(doc) {
462 removeMarkersFromMap(doc);
463
464 delete _docList[nodeID];
465
466 var filtered_ids_to_keep = [];
467 for(var j = 0; j < _docList.ids.length; j++) {
468 if(_docList.ids[j] !== nodeID) {
469 filtered_ids_to_keep.push(_docList.ids[j]);
470 }
471 }
472
473 _docList.ids = filtered_ids_to_keep;
474
475 } else {
476 console.log("**** In toggleMapSection: shouldn't happen - failed to find doc on closing node: " + nodeID);
477 }
478 }
479
480 }
481 }
482
483 _debugPrintDocList();
484
485 mapEnabled = (_docList.ids.length > 0);
486
487 if(mapEnabled) {
488 showMap("toggleMapSection");
489 } else {
490 hideMap("toggleMapSection");
491 }
492
493
494}
495
496/* Given a doc, removes all its shapes doc.shapes from the map _map */
497function removeMarkersFromMap(doc) {
498 for(var i = 0; i < doc.shapes.length; i++) {
499 var shape = doc.shapes[i];
500 shape.setMap(null);
501 }
502}
503
504// Refer to https://developers.google.com/maps/documentation/javascript/reference/map#Map.fitBounds
505// In order for fitBounds() to work out a non-zero bounds, have to use visibility, not display when hiding/showing the map.
506// AND should not set height to 0px when using visibility hidden. But not setting height to 0 means an invisible map takes up space.
507// In order for the resulting invisible map, that still takes up space, to not break the flow of the visible items
508// on the page, need to swap map between position relative when map visible versus position absolute when the map is hidden.
509function showMap(callingFunction) {
510
511 //$("#map_canvas").css({display:"block"});
512 //$("#map_canvas").css({visibility:"visible", height:"100%"}); // not working in conjunction with hidden 0px
513 //$("#map_canvas").css({visibility:"visible", height: "400px"}); // but this works in conjunction with hidden 0px
514 $("#map_canvas").css({visibility:"visible", position: "relative"});
515
516 if(!_DEBUGGING_) return;
517
518 //var message = "map_canvas display BLOCK";
519 var message = "map_canvas visibility VISIBLE, position relative";
520 if(typeof callingFunction !== 'undefined') {
521 message = "showMap() called from" + callingFunction + ":" + message;
522 }
523 console.log("### " + message);
524}
525
526function hideMap(callingFunction) {
527 //$("#map_canvas").css({display:"none"});
528 //$("#map_canvas").css({visibility:"hidden", height:"0px"});
529 $("#map_canvas").css({visibility:"hidden", position:"absolute"});
530
531 if(!_DEBUGGING_) return;
532
533 //var message = "map_canvas display NONE";
534 var message = "map_canvas visibility HIDDEN, position absolute";
535 if(typeof callingFunction !== 'undefined') {
536 message = "hideMap() called from" + callingFunction + ":" + message;
537 }
538 console.log("### " + message);
539}
540
541function updateMap()
542{
543 //console.log("@@@ updateMap()");
544 var markersOnMap = 0;
545 var bounds = new google.maps.LatLngBounds();
546
547 for(var i = 0; i < _docList.ids.length; i++)
548 {
549 var doc = _docList.getDocByIndex(i);
550 if(doc.parentCL && doc.parentCL.style.display == "none")
551 {
552 if(doc.shapes) {
553 for(var x = 0; x < doc.shapes.length; x++) {
554 doc.shapes[x].setVisible(false);
555 }
556 } else {
557 doc.marker.setVisible(false);
558 }
559 continue;
560 }
561 else
562 {
563 if(doc.shapes) {
564 for(var x = 0; x < doc.shapes.length; x++) {
565 doc.shapes[x].setVisible(true);
566 markersOnMap += ShapesUtil.numberOfCoordinatesInBounds(doc.shapes[x]);
567 }
568 } else {
569 doc.marker.setVisible(true);
570 markersOnMap++;
571 }
572
573 }
574
575 if(doc.shapes) {
576 var docSection_overlay_bounds = ShapesUtil.overlayBounds(doc.shapes);
577 // We now have the current document or document subsection's bounds.
578 // Use this to extend the overall bounds (the cumulative bounds for all nearby documents,
579 // or at least the cumulative bounds for this document with all its subsections).
580 bounds.extend(docSection_overlay_bounds.getNorthEast());
581 bounds.extend(docSection_overlay_bounds.getSouthWest());
582 }
583 else {
584 var doc_latlng = new google.maps.LatLng(doc.lat, doc.lng);
585 bounds.extend(doc_latlng);
586 }
587 }
588
589 _debugPrintBounds(bounds, "@@@ UpdateMap():");
590
591
592 if(markersOnMap > 1)
593 {
594 _debugPrintBounds(_map.getBounds(), "@@@ UpdateMap() : BEFORE fitbounds, map");
595
596 _map.fitBounds(bounds);
597
598 _debugPrintBounds(_map.getBounds(), "@@@ UpdateMap() : AFTER fitbounds, map");
599 } else if (markersOnMap == 1) {
600 //console.log("@@@ updating bounds with " + markersOnMap + " markers on the map");
601 //console.log(bounds);
602
603 // sometimes a single point bounds are too small for the map to display, so use center and zoom instead of fitbounds.
604 _map.setCenter(bounds.getCenter());
605 _map.setZoom(18); // arbitrary value that looked nice for my example
606 }
607}
608
609
610// TODO: FUNCTION DUPLICATED IN panoramaViewer.js
611function getLatLngForCoord(coord) {
612
613 // https://stackoverflow.com/questions/2559318/how-to-check-for-an-undefined-or-null-variable-in-javascript
614 if(!coord) {
615 // some_variable is either null, undefined, 0, NaN, false, or an empty string
616 console.log("@@@@ In map-scripts::getLatLngForCoord(): no or invalid coord info");
617 return null;
618 }
619
620 // coord is of the form: "37S77 157E53"
621 // lat will be 37S77, lng 157E53.
622 var indexOfSpace = coord.indexOf(" ");
623 if(indexOfSpace === -1) {
624 console.log("@@@@ In map-scripts::getLatLngForCoord(): bad format for coord " + coord);
625 return null;
626 }
627 var latitude = coord.substring(0, indexOfSpace);
628 var longitude = coord.substring(indexOfSpace+1);
629 return {lat: latitude, lng: longitude}; // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Working_with_Objects
630}
631
632function loopThroughMarkers()
633{
634 if(_docList.ids.length == 0)
635 {
636 return;
637 }
638
639 var doc;
640 var visibleMarkers = new Array();
641 for(var i = 0; i < _docList.ids.length; i++)
642 {
643 //var doc = _docList.getDocByIndex(i);
644 // NOTE: in JavaScript, "local" vars have function scope, not mere block level scope. Wherever declared inside a function, they get hoisted to function top.
645 // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var
646 // But this feature is confusing, so have now declared var doc nearer to function top, at function level so outside current local block, and am assigning here:
647 doc = _docList.getDocByIndex(i);
648 if(typeof doc.shapes !== 'undefined') {
649 for(var x = 0; x < doc.shapes.length; x++) {
650 var shape = doc.shapes[x];
651 if(shape.type === google.maps.drawing.OverlayType.MARKER && shape.getVisible())
652 {
653 visibleMarkers.push(doc);
654 }
655 }
656 }
657 if(doc.marker && doc.marker.getVisible())
658 {
659 visibleMarkers.push(doc);
660 }
661 }
662
663 if(visibleMarkers.length < 2)
664 {
665 clearAllInfoBoxes();
666 return;
667 }
668
669 clearAllInfoBoxes();
670
671 var elem = null;
672 while(!elem) // TODO: why redefine elem? Why does this work, and only this, but not while(true) or while(doc.marker) or while(!AnythingFalse)???
673 // Some clever behaviour here, but no documentation on the cleverness. Hard to understand
674 {
675 if(_docList.loopIndex >= visibleMarkers.length)
676 {
677 _docList.loopIndex = 0;
678 }
679
680 //var doc = visibleMarkers[_docList.loopIndex]; // See NOTE above.
681 doc = visibleMarkers[_docList.loopIndex];
682 elem = gs.jqGet("div" + doc.nodeID); // This used to redefine elem by doing var elem = <....>
683 // This worked because "If you re-declare a JavaScript variable, it will not lose its value." See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var
684 if(elem.length)
685 {
686 elem.css("background", "#BBFFBB");
687 setTimeout(function(){elem.css("background", "");}, 2000);
688 }
689 _docList.loopIndex ++;
690 }
691
692 _gsDebug("@@@ loopThroughmarkers() - DOC:", doc);
693
694 if(doc.marker) {
695 doc.marker.markerInfo.open(_map, doc.marker); // TODO: how does doc have a value here? Where is the value set at this block level?
696 }
697
698 if(doc.shapes) {
699 for(var x = 0; x < doc.shapes.length; x++) {
700 var shape = doc.shapes[i];
701 if(shape.type === google.maps.drawing.OverlayType.MARKER) {
702 shape.markerInfo.open(_map, shape);
703 } else {
704 shape.markerInfo.open(_map);
705 }
706 }
707 }
708}
709
710
711function focusDocument(id)
712{
713 var doc = _docList[id];
714 if(doc)
715 {
716 clearInterval(_intervalHandle);
717 _intervalHandle = null;
718
719 if(doc.shapes) {
720 var docShapesBounds = ShapesUtil.overlayBounds(doc.shapes);
721 _map.panToBounds(docShapesBounds); // https://developers.google.com/maps/documentation/javascript/reference/map#Map.panToBounds
722 } else {
723 _map.panTo(new google.maps.LatLng(doc.lat, doc.lng));
724 }
725
726 clearAllInfoBoxes();
727 if(doc.shapes) { // TODO
728 //console.log("Opening infowindow for doc " + doc.nodeID);
729 for(var x = 0; x < doc.shapes.length; x++) {
730 if(doc.shapes[x].markerInfo) {
731 doc.shapes[x].markerInfo.open(_map); // label
732 }
733 else {
734 console.log("No infowindow for doc " + doc.nodeID + "'s shape " + doc.shapes[x].type);
735 }
736 }
737 //openInfoBoxes(doc);
738 } else { // only Lat and Lng meta, so we have just one marker per doc, which will have the doc title not label
739 doc.marker.markerInfo.open(_map, doc.marker); // doc title
740 }
741 var scrollCheckbox = $("#scrollCheckbox");
742 if(scrollCheckbox.checked)
743 {
744 scrollCheckbox.checked = false;
745 }
746 }
747}
748
749function clearAllInfoBoxes()
750{
751 for(var i = 0; i < _docList.ids.length; i++)
752 {
753 var doc = _docList.getDocByIndex(i);
754
755 if(doc.shapes) {
756 for(var x = 0; x < doc.shapes.length; x++) {
757 if(doc.shapes[x].markerInfo) {
758 //console.log("Closing infowindow for doc " + _docList.ids[i]);
759 doc.shapes[x].markerInfo.close();
760 }
761 }
762 }
763 else { // only Lat and Lng meta, so we have just one marker per doc
764 doc.marker.markerInfo.close();
765 }
766 }
767}
768
769function createOverlayItems(doc, options) {
770 var loopCounter = 0;
771 if(doc.mapoverlay || doc.childrenMapoverlays) {
772 if(doc.mapoverlay) {
773 // append any child section's mapoverlays onto doc.mapoverlay
774 if(doc.childrenMapoverlays) {
775 for(loopCounter = 0; loopCounter < doc.childrenMapoverlays.length; loopCounter++) {
776 //console.log("Pushing next child mapoverlay onto doc.mapoverlay: ", doc.childrenMapoverlays[loopCounter]);
777 Array.prototype.push.apply(doc.mapoverlay, doc.childrenMapoverlays[loopCounter]);
778 }
779 delete doc.childrenMapoverlays; // served its purpose
780 }
781 } else if (doc.childrenMapoverlays) { // no doc.mapoverlay
782 // construct doc.mapoverlay to contain each child section's mapoverlay
783 doc.mapoverlay = [];
784 for(loopCounter = 0; loopCounter < doc.childrenMapoverlays.length; loopCounter++) {
785 //console.log("Pushing next child mapoverlay onto originally empty doc.mapoverlay: ", doc.childrenMapoverlays[loopCounter]);
786 Array.prototype.push.apply(doc.mapoverlay, doc.childrenMapoverlays[loopCounter]);
787 }
788 delete doc.childrenMapoverlays; // no more use for this
789 }
790 //console.log("@@@@ Have shapes: ", doc.mapoverlay);
791 createShapes(doc, options);
792 } else { // backwards compatible to deal with Lat and Lng meta stored for doc
793 pos = new google.maps.LatLng(doc.lat,doc.lng);
794 createMarker(doc, pos, options);
795 }
796}
797
798function addInfoMarker(doc, shape) {
799
800 if(!shape.description) {
801 _gsInfo("#### " + shape.type.toString() + " had no description/label");
802 return;
803 }
804
805 // else add an InfoWindow for this shape using the label (shape.description)
806
807 // https://developers.google.com/maps/documentation/javascript/infowindows
808 // An InfoWindow's "position contains the LatLng at which this info window is anchored.
809 // Note: An InfoWindow may be attached either to a Marker object (in which case its position is based on the marker's location)
810 // or on the map itself at a specified LatLng. Opening an info window on a marker will automatically update the position."
811 var infoWindow = new google.maps.InfoWindow({content:shape.description}); // NOTE: if not setting content or position properties
812 // inside this constructor, need to call setContent/setPosition to set them
813
814 if(shape.type === google.maps.drawing.OverlayType.MARKER) {
815 var marker = shape;
816 _gsDebug("Coord for marker is " + marker.getPosition().toString());
817
818 marker.addListener('mouseover', function() {
819 infoWindow.open(_map, marker);
820 });
821 marker.addListener('mouseout', function() {
822 infoWindow.close();
823 });
824 attachClickHandler(marker, doc.nodeID); // do what the original code used to do here
825 }
826 else {
827 var coord = ShapesUtil.getLabelCoordinate(shape);
828 _gsDebug("Coord for " + shape.type.toString() + " is " + coord.toString());
829 infoWindow.setPosition(coord);
830 shape.addListener('mouseover', function() {
831 infoWindow.open(_map);
832 });
833 shape.addListener('mouseout', function() {
834 infoWindow.close();
835 });
836 attachClickHandler(shape, doc.nodeID); // as above
837 }
838 shape.markerInfo = infoWindow;
839 //console.log("######## Added markerInfo object to shape");
840}
841
842// This function will create Google Shapes/Overlays and markers out of a given doc JSONNode's doc.mapOverlay
843// (doc.mapOverlay shapes are stored as an array of JSON) and store the shapes/overlays in the doc.shapes array.
844function createShapes(doc, options)
845{
846 var isMainDoc = options["mainDoc"];
847
848 // for doc.shapes: don't store JSON anymore, convert them to google Shapes overlays and store them instead
849 doc.shapes = [];
850
851 for (var i=0; i<doc.mapoverlay.length; i++) {
852 //console.log("in: mapoverlay["+i+"] =" + JSON.stringify(doc.mapoverlay[i]));
853 var shape = ShapesUtil.JSONToShape(doc.mapoverlay[i]);
854 //console.log("out: shape = " + JSON.stringify(shape));
855
856 doc.shapes[i] = shape;
857 shape.setMap(_map);
858 //shape["title"] = doc.title; // TODO: Think on it some more.
859
860 // Unset editable and draggable properties of shape
861 // And for markers, which are initialised to clickable besides, undo the clickability
862 // and set them
863 if(shape.type === google.maps.drawing.OverlayType.MARKER) {
864 var marker = shape;
865 // markers of the main document should be red, else they'll be blue
866 if(!isMainDoc) {
867 marker["icon"] = "interfaces/" + gs.xsltParams.interface_name + "/images/bluemarker.png";
868 }
869 marker.clickable = false; // only markers
870 /*
871 console.log("@@@ map-scripts::addInfoMarker - marker.position");
872 console.log("Lat is " + typeof(marker.position.lat()));
873 console.log(marker.position.lat());
874 console.log("Long is " + typeof(marker.position.lng()));
875 console.log(marker.position.lng());
876 */
877 } else {
878 //console.log("Creating non-marker shape.");
879
880 if(!isMainDoc) {
881 ShapesUtil.setOpacity(shape, LOW_OPACITY);
882 } // else the shape will be drawn at its configured opacity
883 }
884
885 shape.editable = false;
886 shape.draggable = false;
887
888
889 // doc[i]'s label = doc.shapes[i].description
890 addInfoMarker(doc, shape);
891 }
892
893 var docElement = gs.jqGet("div" + doc.nodeID);
894 var parent;
895 if(docElement)
896 {
897 parent = docElement.parentNode;
898 }
899
900 while(parent && parent.nodeName != "BODY")
901 {
902 if($(parent).attr("id") && $(parent).attr("id").search("divCL") != -1)
903 {
904 doc.parentCL = parent;
905 break;
906 }
907
908 parent = parent.parentNode;
909 }
910}
911
912// This method is only for backwards compatibility: for those collections with docs that only have Lat and Lng meta
913// and no GPS.mapOverlay (and hence Coordinate) meta.
914function createMarker(doc, pos, options)
915{
916 var isMainMarker = options["mainDoc"];
917
918 var marker;
919 if(isMainMarker)
920 {
921 marker = new google.maps.Marker
922 ({
923 position: pos,
924 title:doc.title,
925 map:_map
926 });
927 }
928 else
929 {
930 marker = new google.maps.Marker
931 ({
932 position: pos,
933 title:doc.title,
934 map:_map,
935 icon:"interfaces/" + gs.xsltParams.interface_name + "/images/bluemarker.png"
936 });
937 }
938
939 var docElement = gs.jqGet("div" + doc.nodeID);
940 var parent;
941 if(docElement)
942 {
943 parent = docElement.parentNode;
944 }
945
946 while(parent && parent.nodeName != "BODY")
947 {
948 if($(parent).attr("id") && $(parent).attr("id").search("divCL") != -1)
949 {
950 doc.parentCL = parent;
951 break;
952 }
953
954 parent = parent.parentNode;
955 }
956
957 var info = new google.maps.InfoWindow({content:doc.title});
958 marker.markerInfo = info;
959 doc.marker = marker;
960 attachClickHandler(marker, doc.nodeID);
961}
962
963// TODO: with the following, it seems that clicking on shape expands the entire document
964// Should it be that clicking on a shape should expand the doc section that contains that shape meta?
965function attachClickHandler(shapeOrMarker, nodeID)
966{
967 google.maps.event.addListener(shapeOrMarker, 'click', function()
968 {
969 document.location.href = gs.xsltParams.library_name + "?a=d&ed=1&c=" + gs.cgiParams.c + "&d=" + nodeID + "&dt=hierarchy&p.a=b&p.sa=&p.s=ClassifierBrowse";
970 });
971}
972
973function NewLatLng(lat, lng)
974{
975 console.log("Latitude " + lat);
976 console.log("Longitude " + lng);
977}
978
979function httpMapBrowseRequest(sectionID)
980{
981 // Make ajax call to retrieve jsonNodes for section ID, and draw shapes on the map.
982 var url = gs.xsltParams.library_name + "?a=b&rt=s&s=ClassifierBrowse&c=" + gs.cgiParams.c + "&cl=" + sectionID + "&excerptid=jsonNodes";
983 $.ajax(url)
984 .success(function(responseText)
985 {
986 var startIndex = responseText.indexOf(">");
987 var endIndex = responseText.indexOf("</");
988
989 var jsonNodesStr = responseText.substring(startIndex+1, endIndex);
990 var jsonNodes = eval(jsonNodesStr); //responseText.substring(startIndex+1, endIndex));
991 if(jsonNodes && jsonNodes.length > 0)
992 {
993
994 mapEnabled = true;
995 showMap("httpMapBrowseRequest");
996
997 for(var i = 0; i < jsonNodes.length; i++)
998 {
999 var doc = jsonNodes[i];
1000 _docList[doc.nodeID] = doc;
1001 _docList.ids.push(doc.nodeID);
1002
1003 var options = {
1004 "mainDoc": true // TODO: should this be true or false???
1005 };
1006 createOverlayItems(doc, options);
1007 }
1008
1009 ///var tmp = $("#title"+sectionID);
1010 ///console.log(tmp); // WRONG
1011 ///var tmp2 = document.getElementById("title"+sectionID);
1012 ///console.log(tmp2); // RIGHT, WHY?
1013
1014 // create data-* attribute to store this sectionID's JSON on the section's div
1015 //$("#title"+sectionID).attr("data-gps-map-json", "hello world"); // TODO: Doesn't work. Why?
1016 var titleClassifierEl = document.getElementById("title"+sectionID);
1017 titleClassifierEl.setAttribute("data-gps-map-json", jsonNodesStr);
1018 }
1019
1020 updateMap();
1021 //console.log("getSub Classifier -> updateMap()");
1022 })
1023 .error(function()
1024 {
1025 //console.log("Error getting subclassifiers");
1026 return;
1027 });
1028}
1029
1030function performDistanceSearchWithCoordinates(id, coord, degrees)
1031{
1032 var coordInfo = getLatLngForCoord(coord);
1033 if(!coordInfo) {
1034 console.log("@@@ ERROR in map-scripts::performDistanceSearchWithCoordinates: coordInfo is null");
1035 }
1036 performDistanceSearch(id, coordInfo.lat, coordInfo.lng, degrees);
1037}
1038
1039function performDistanceSearch(id, lat, lng, degrees)
1040{
1041 if(parseFloat(lat) > 180 || parseFloat(lat) < -180 || parseFloat(lng) > 180 || parseFloat(lat) < -180)
1042 {
1043 console.log("Latitude or longitude incorrectly formatted");
1044 return;
1045 }
1046
1047 if(lat.indexOf(".") == -1 || lng.indexOf(".") == -1 || (lat.indexOf(".") + 3) >= lat.length || (lng.indexOf(".") + 3) >= lng.length)
1048 {
1049 console.log("Latitude or longitude does not have the required precision for a distance search");
1050 return;
1051 }
1052
1053 var query = "";
1054 for(var i = 0; i < degrees * 2; i++)
1055 {
1056 for (var j = 0; j < degrees * 2; j++)
1057 {
1058 var latDelta = (i - degrees) * 0.01;
1059 var lngDelta = (j - degrees) * 0.01;
1060
1061 //query += "(" + getDistanceQueryStringOldApproach(lat, latDelta, 2, "LA", ["N","S"]);
1062 //query += "+AND+";
1063 //query += getDistanceQueryStringOldApproach(lng, lngDelta, 2, "LN", ["E","W"]) + ")";
1064
1065 query += "(" + getDistanceQueryStringTerm(lat, lng, latDelta, lngDelta, 2, "CS") + ")";
1066
1067 if(i != ((degrees * 2) - 1) || j != ((degrees * 2) - 1)){ query += "+OR+"; }
1068 }
1069 }
1070
1071 var inlineTemplate = '\
1072 <xsl:template match="/" priority="5">\
1073 <table id="nearbyDocs">\
1074 <tr>\
1075 <th><a href="javascript:sortByDistance();">Distance</a></th><th><a href="javascript:sortAlphabetically();">Document</a></th>\
1076 </tr>\
1077 <xsl:apply-templates select="//documentNode"/>\
1078 </table>\
1079 </xsl:template>\
1080 \
1081 <xsl:template match="documentNode" priority="5">\
1082 <xsl:if test="@nodeID !=\''+id+'\'">\
1083 <tr>\
1084 <td>___<gsf:metadata name="Latitude"/>______<gsf:metadata name="Longitude"/>___</td>\
1085 <td><gsf:link title="'+gs.text.doc.nearby_doc_tooltip+'" type="document"><gsf:metadata name="Title"/></gsf:link></td>\
1086 </tr>\
1087 </xsl:if>\
1088 </xsl:template>';
1089
1090 var url = gs.xsltParams.library_name + "?a=q&s=RawQuery&rt=rd&c=" + gs.cgiParams.c + "&s1.rawquery=" + query + "&excerptid=nearbyDocs&ilt=" + inlineTemplate.replace(/ /, "%20");
1091 $.ajax(url)
1092 .success(function(response)
1093 {
1094 response = response.replace(/<img src="[^"]*map_marker.png"[^>]*>/g, "");
1095
1096 var nearbyDocsArray = new Array();
1097
1098 var lats = new Array();
1099 var lngs = new Array();
1100 var matches = response.match(/___(-?[0-9\.]*)___/g);
1101 for(var i = 0; i < matches.length; i += 2)
1102 {
1103 var matchLatFloat = parseFloat(matches[i].replace("___", ""));
1104 var matchLngFloat = parseFloat(matches[i+1].replace("___", ""));
1105
1106 lats.push(matchLatFloat);
1107 lngs.push(matchLngFloat);
1108 var distance = Math.sqrt(Math.pow(matchLatFloat - parseFloat(lat), 2) + Math.pow(matchLngFloat - parseFloat(lng), 2)) * (40000.0/360.0);
1109 var distanceString = "" + distance;
1110 distanceString = distanceString.substring(0, 6);
1111 response = response.replace(matches[i] + matches[i+1], distanceString);
1112 }
1113
1114 var index = 0;
1115 var i = 0;
1116 while(true)
1117 {
1118 var distanceStart = response.indexOf("<td>", index);
1119 if(distanceStart == -1)
1120 {
1121 break;
1122 }
1123 var distanceEnd = response.indexOf("</td>", distanceStart);
1124
1125 var docLinkStart = response.indexOf("<td>", distanceEnd);
1126 var docLinkEnd = response.indexOf("</td>", docLinkStart);
1127
1128 var dist = response.substring(distanceStart + 4, distanceEnd);
1129 var docLink = response.substring(docLinkStart + 4, docLinkEnd);
1130
1131 _nearbyDocsByDistance.push({title:docLink, distance:dist, lat:lats[i], lng:lngs[i++]});
1132
1133 index = docLinkEnd;
1134 }
1135
1136 sortByDistance(lat,lng);
1137
1138 var toggle = $("#nearbyDocumentsToggle");
1139 toggle.attr("src", gs.imageURLs.collapse);
1140 gs.functions.makeToggle(toggle, $("#nearbyDocuments"));
1141 });
1142}
1143
1144var map_centering_timeout = null;
1145function recenterMapF(lat, lng)
1146{
1147 return function() {
1148 _map.setCenter(new google.maps.LatLng(lat, lng));
1149 }
1150}
1151function recenterMap(lat, lng)
1152{
1153
1154 _map.setCenter(new google.maps.LatLng(lat, lng));
1155
1156}
1157function sortByDistance(base_lat,base_lng)
1158{
1159 var sortedTable = '<table id="nearbyDocs" onmouseleave="clearTimeout(map_centering_timeout); recenterMap('+base_lat+','+base_lng+');"><tr><th><a href="javascript:;">Distance</a></th><th><a href="javascript:sortAlphabetically('+base_lat+', '+base_lng+');">Document</a></th></tr>';
1160 _nearbyDocsByDistance.sort(function(a, b){return (a.distance - b.distance);});
1161 for(var i = 0; i < _nearbyDocsByDistance.length; i++)
1162 {
1163
1164 sortedTable += "<tr><td>" + prettifyDistance(_nearbyDocsByDistance[i].distance) + '</td><td onmouseover="clearTimeout(map_centering_timeout); map_centering_timeout = setTimeout(recenterMapF(' + _nearbyDocsByDistance[i].lat + ',' + _nearbyDocsByDistance[i].lng + '), 900)" >' + _nearbyDocsByDistance[i].title + "</td></tr>";
1165 }
1166 sortedTable += "</table>";
1167
1168 $("#nearbyDocuments").html(sortedTable);
1169}
1170function prettifyDistance(distance) {
1171
1172 var new_distance;
1173 if (distance < 1) {
1174 new_distance = (distance * 1000);
1175 // Round to nearest whole number - don't need to show points of metres..
1176 new_distance = Math.round(new_distance);
1177 new_distance += " m";
1178 }
1179 else {
1180 new_distance = distance +" km";
1181
1182 }
1183 return new_distance;
1184}
1185
1186
1187
1188function sortAlphabetically(base_lat, base_lng)
1189{
1190 var sortedTable = '<table id="nearbyDocs" onmouseleave="clearTimeout(map_centering_timeout); recenterMap('+base_lat+','+base_lng+');"><tr><th><a href="javascript:sortByDistance('+base_lat+', '+base_lng+');">Distance</a></th><th><a href="javascript:;">Document</a></th></tr>';
1191 _nearbyDocsByDistance.sort(function(a, b)
1192 {
1193 var firstTitleStartIndex = a.title.indexOf(">");
1194 var firstTitleEndIndex = a.title.indexOf("<", firstTitleStartIndex);
1195 var firstTitle = a.title.substring(firstTitleStartIndex + 1, firstTitleEndIndex);
1196 var secondTitleStartIndex = b.title.indexOf(">");
1197 var secondTitleEndIndex = b.title.indexOf("<", secondTitleStartIndex);
1198 var secondTitle = b.title.substring(secondTitleStartIndex + 1, secondTitleEndIndex);
1199 return ((firstTitle.toLowerCase() == secondTitle.toLowerCase()) ? 0 : ((firstTitle.toLowerCase() > secondTitle.toLowerCase()) ? 1 : -1));
1200 });
1201 for(var i = 0; i < _nearbyDocsByDistance.length; i++)
1202 {
1203 sortedTable += "<tr><td>" + _nearbyDocsByDistance[i].distance + '</td><td onmouseover="clearTimeout(map_centering_timeout); map_centering_timeout = setTimeout(recenterMapF(' + _nearbyDocsByDistance[i].lat + ',' + _nearbyDocsByDistance[i].lng + '), 900)">' + _nearbyDocsByDistance[i].title + "</td></tr>";
1204 }
1205 sortedTable += "</table>";
1206
1207 $("#nearbyDocuments").html(sortedTable);
1208}
1209
1210function getDistanceQueryStringOldApproach(currentCoord, delta, precision, indexName, directions)
1211{
1212 console.error("**** Old Approach called!!!");
1213
1214 var query = "";
1215 var coordFloat = parseFloat(currentCoord);
1216
1217 var newCoord = "" + (coordFloat + delta);
1218 var beforeDec = newCoord.substring(0, newCoord.indexOf("."));
1219
1220 var direction = directions[0];
1221 if(coordFloat < 0)
1222 {
1223 // negative value
1224 direction = directions[1];
1225 beforeDec = beforeDec.substring(1); // skip over '-' at front
1226 }
1227 beforeDec += direction;
1228
1229 var afterDec = newCoord.substring(newCoord.indexOf(".") + 1, newCoord.indexOf(".") + (precision) + 1);
1230
1231 return indexName + ":" + beforeDec + "+AND+" + indexName + ":" + afterDec;
1232}
1233
1234function coordValToIndexToken(coordValStr, delta, precision, directions)
1235{
1236 var coordValFloat = parseFloat(coordValStr);
1237
1238 var deltaCoordValStr = "" + (coordValFloat + delta);
1239 var beforeDec = deltaCoordValStr.substring(0, deltaCoordValStr.indexOf("."));
1240
1241 var direction = directions[0];
1242 if(coordValFloat < 0)
1243 {
1244 // negative value
1245 direction = directions[1];
1246 beforeDec = beforeDec.substring(1); // skip over '-' at front
1247 }
1248
1249 var beforeDecWithDirection = beforeDec + direction;
1250
1251 var afterDecPrecision = deltaCoordValStr.substring(deltaCoordValStr.indexOf(".") + 1, deltaCoordValStr.indexOf(".") + (precision) + 1);
1252
1253 var indexToken = beforeDecWithDirection + afterDecPrecision;
1254
1255 return indexToken;
1256}
1257
1258function getDistanceQueryStringTerm(currentLat,currentLng, deltaLat, deltaLng, precision, indexName)
1259{
1260 var latToken = coordValToIndexToken(currentLat,deltaLat,precision,["N","S"]);
1261 var lngToken = coordValToIndexToken(currentLng,deltaLng,precision,["E","W"]);
1262
1263 var queryStringTerm = indexName + ":\"" + latToken + " " + lngToken + "\"";
1264
1265 return queryStringTerm;
1266}
1267
1268function _gsInfo(message, optObject) {
1269 console.log(message);
1270 if(typeof optObject !== 'undefined') {
1271 console.log(optObject);
1272 }
1273}
1274
1275function _gsDebug(message, optObject) {
1276 if(_DEBUGGING_) {
1277 _gsInfo(message, optObject);
1278 }
1279}
1280
1281
1282function _debugPrintDocList() {
1283 if(!_DEBUGGING_) return;
1284
1285 console.log("@@@@ printing docList:");
1286 for(var i = 0; i < _docList.ids.length; i++)
1287 {
1288 var doc = _docList.getDocByIndex(i);
1289 console.log(" At index = " + i + " ids[i]: " + _docList.ids[i]);
1290 console.log(" At index = " + i + " ids[i]: " + _docList.ids[i] + ", doc: " + doc.nodeID);
1291 }
1292}
1293
1294function _debugPrintBounds(bounds, message) {
1295 if(!_DEBUGGING_) return;
1296 var ne = bounds.getNorthEast();
1297 var sw = bounds.getSouthWest();
1298 console.log(message + " bounds: ne = " + ne + ", sw = " + sw);
1299}
Note: See TracBrowser for help on using the repository browser.