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

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

Incorporated request for GPS.mapOverlay for children of a section, following Kathy's explanation from last Thursday. map-scripts then adds the overlays for each child to its parent section's doc.mapoverlay. That seems to work for the solr-haminfo GPS map data demo collection. But Solr faceted searching still doesn't work the way I understand it should. And of course, we don't just want children of a section but all descendants and the section itself.

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