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

Last change on this file since 33290 was 33290, checked in by wy59, 5 years ago

Minor cleanup

  • Property svn:executable set to *
File size: 37.3 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
360 var sectionID = options["nodeID"];
361
362 var titleClassifierEl = document.getElementById("title"+sectionID);
363 var jsonNodesStr = titleClassifierEl.getAttribute("data-gps-map-json");
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 visibleMarkers = new Array();
575 for(var i = 0; i < _docList.ids.length; i++)
576 {
577 var doc = _docList.getDocByIndex(i);
578 if(typeof doc.shapes !== 'undefined') {
579 for(var x = 0; x < doc.shapes.length; x++) {
580 var shape = doc.shapes[x];
581 if(shape.type === google.maps.drawing.OverlayType.MARKER && shape.getVisible())
582 {
583 visibleMarkers.push(doc);
584 }
585 }
586 }
587 if(doc.marker && doc.marker.getVisible())
588 {
589 visibleMarkers.push(doc);
590 }
591 }
592
593 if(visibleMarkers.length < 2)
594 {
595 clearAllInfoBoxes();
596 return;
597 }
598
599 clearAllInfoBoxes();
600
601 var elem = null;
602 while(!elem) // TODO: why redefine elem? Why does this work, and only this, but not while(true) or while(doc.marker) or while(!AnythingFalse)???
603 // Some clever behaviour here, but no documentation on the cleverness. Hard to understand
604 {
605 if(_docList.loopIndex >= visibleMarkers.length)
606 {
607 _docList.loopIndex = 0;
608 }
609
610 var doc = visibleMarkers[_docList.loopIndex];
611 elem = gs.jqGet("div" + doc.nodeID); // TODO: this used to redefine elem by doing var elem = <....>
612 if(elem.length)
613 {
614 elem.css("background", "#BBFFBB");
615 setTimeout(function(){elem.css("background", "");}, 2000);
616 }
617 _docList.loopIndex ++;
618 }
619 //console.log("@@@@ DOC:");
620 //console.log(doc);
621
622 if(doc.marker) {
623 doc.marker.markerInfo.open(_map, doc.marker); // TODO: how does doc have a value here? Where is the value set at this block level?
624 }
625
626 if(doc.shapes) {
627 for(var x = 0; x < doc.shapes.length; x++) {
628 var shape = doc.shapes[i];
629 if(shape.type === google.maps.drawing.OverlayType.MARKER) {
630 shape.markerInfo.open(_map, shape);
631 } else {
632 shape.markerInfo.open(_map);
633 }
634 }
635 }
636}
637
638
639function focusDocument(id)
640{
641 var doc = _docList[id];
642 if(doc)
643 {
644 clearInterval(_intervalHandle);
645 _intervalHandle = null;
646
647 if(doc.shapes) {
648 var docShapesBounds = ShapesUtil.overlayBounds(doc.shapes);
649 _map.panToBounds(docShapesBounds); // https://developers.google.com/maps/documentation/javascript/reference/map#Map.panToBounds
650 } else {
651 _map.panTo(new google.maps.LatLng(doc.lat, doc.lng));
652 }
653
654 clearAllInfoBoxes();
655 if(doc.shapes) { // TODO
656 //console.log("Opening infowindow for doc " + doc.nodeID);
657 for(var x = 0; x < doc.shapes.length; x++) {
658 if(doc.shapes[x].markerInfo) {
659 doc.shapes[x].markerInfo.open(_map); // label
660 }
661 else {
662 console.log("No infowindow for doc " + doc.nodeID + "'s shape " + doc.shapes[x].type);
663 }
664 }
665 //openInfoBoxes(doc);
666 } else { // only Lat and Lng meta, so we have just one marker per doc, which will have the doc title not label
667 doc.marker.markerInfo.open(_map, doc.marker); // doc title
668 }
669 var scrollCheckbox = $("#scrollCheckbox");
670 if(scrollCheckbox.checked)
671 {
672 scrollCheckbox.checked = false;
673 }
674 }
675}
676
677function clearAllInfoBoxes()
678{
679 for(var i = 0; i < _docList.ids.length; i++)
680 {
681 var doc = _docList.getDocByIndex(i);
682
683 if(doc.shapes) {
684 for(var x = 0; x < doc.shapes.length; x++) {
685 if(doc.shapes[x].markerInfo) {
686 //console.log("Closing infowindow for doc " + _docList.ids[i]);
687 doc.shapes[x].markerInfo.close();
688 }
689 }
690 }
691 else { // only Lat and Lng meta, so we have just one marker per doc
692 doc.marker.markerInfo.close();
693 }
694 }
695}
696
697function createOverlayItems(doc, options) {
698 if(doc.mapoverlay) {
699 //console.log("Have shapes: " + doc.mapoverlay.toString());
700 createShapes(doc, options);
701 } else { // backwards compatible to deal with Lat and Lng meta stored for doc
702 pos = new google.maps.LatLng(doc.lat,doc.lng);
703 createMarker(doc, pos, options);
704 }
705}
706
707function addInfoMarker(doc, shape) {
708
709 if(!shape.description) {
710 _gsInfo("#### " + shape.type.toString() + " had no description/label");
711 return;
712 }
713
714 // else add an InfoWindow for this shape using the label (shape.description)
715
716 // https://developers.google.com/maps/documentation/javascript/infowindows
717 // An InfoWindow's "position contains the LatLng at which this info window is anchored.
718 // Note: An InfoWindow may be attached either to a Marker object (in which case its position is based on the marker's location)
719 // or on the map itself at a specified LatLng. Opening an info window on a marker will automatically update the position."
720 var infoWindow = new google.maps.InfoWindow({content:shape.description}); // NOTE: if not setting content or position properties
721 // inside this constructor, need to call setContent/setPosition to set them
722
723 if(shape.type === google.maps.drawing.OverlayType.MARKER) {
724 var marker = shape;
725 _gsDebug("Coord for marker is " + coord.toString());
726
727 marker.addListener('mouseover', function() {
728 infoWindow.open(_map, marker);
729 });
730 marker.addListener('mouseout', function() {
731 infoWindow.close();
732 });
733 attachClickHandler(marker, doc.nodeID); // do what the original code used to do here
734 }
735 else {
736 var coord = ShapesUtil.getLabelCoordinate(shape);
737 _gsDebug("Coord for " + shape.type.toString() + " is " + coord.toString());
738 infoWindow.setPosition(coord);
739 shape.addListener('mouseover', function() {
740 infoWindow.open(_map);
741 });
742 shape.addListener('mouseout', function() {
743 infoWindow.close();
744 });
745 attachClickHandler(shape, doc.nodeID); // as above
746 }
747 shape.markerInfo = infoWindow;
748 //console.log("######## Added markerInfo object to shape");
749}
750
751// This function will create Google Shapes/Overlays and markers out of a given doc JSONNode's doc.mapOverlay
752// (doc.mapOverlay shapes are stored as an array of JSON) and store the shapes/overlays in the doc.shapes array.
753function createShapes(doc, options)
754{
755 var isMainDoc = options["mainDoc"];
756
757 // for doc.shapes: don't store JSON anymore, convert them to google Shapes overlays and store them instead
758 doc.shapes = [];
759
760 for (var i=0; i<doc.mapoverlay.length; i++) {
761 //console.log("in: mapoverlay["+i+"] =" + JSON.stringify(doc.mapoverlay[i]));
762 var shape = ShapesUtil.JSONToShape(doc.mapoverlay[i]);
763 //console.log("out: shape = " + JSON.stringify(shape));
764
765 doc.shapes[i] = shape;
766 shape.setMap(_map);
767 //shape["title"] = doc.title; // TODO: Think on it some more.
768
769 // Unset editable and draggable properties of shape
770 // And for markers, which are initialised to clickable besides, undo the clickability
771 // and set them
772 if(shape.type === google.maps.drawing.OverlayType.MARKER) {
773 var marker = shape;
774 // markers of the main document should be red, else they'll be blue
775 if(!isMainDoc) {
776 marker["icon"] = "interfaces/" + gs.xsltParams.interface_name + "/images/bluemarker.png";
777 }
778 marker.clickable = false; // only markers
779 /*
780 console.log("@@@ map-scripts::addInfoMarker - marker.position");
781 console.log("Lat is " + typeof(marker.position.lat()));
782 console.log(marker.position.lat());
783 console.log("Long is " + typeof(marker.position.lng()));
784 console.log(marker.position.lng());
785 */
786 } else {
787 //console.log("Creating non-marker shape.");
788
789 if(!isMainDoc) {
790 ShapesUtil.setOpacity(shape, LOW_OPACITY);
791 } // else the shape will be drawn at its configured opacity
792 }
793
794 shape.editable = false;
795 shape.draggable = false;
796
797
798 // doc[i]'s label = doc.shapes[i].description
799 addInfoMarker(doc, shape);
800 }
801
802 var docElement = gs.jqGet("div" + doc.nodeID);
803 var parent;
804 if(docElement)
805 {
806 parent = docElement.parentNode;
807 }
808
809 while(parent && parent.nodeName != "BODY")
810 {
811 if($(parent).attr("id") && $(parent).attr("id").search("divCL") != -1)
812 {
813 doc.parentCL = parent;
814 break;
815 }
816
817 parent = parent.parentNode;
818 }
819}
820
821// This method is only for backwards compatibility: for those collections with docs that only have Lat and Lng meta
822// and no GPS.mapOverlay (and hence Coordinate) meta.
823function createMarker(doc, pos, options)
824{
825 var isMainMarker = options["mainDoc"];
826
827 var marker;
828 if(isMainMarker)
829 {
830 marker = new google.maps.Marker
831 ({
832 position: pos,
833 title:doc.title,
834 map:_map
835 });
836 }
837 else
838 {
839 marker = new google.maps.Marker
840 ({
841 position: pos,
842 title:doc.title,
843 map:_map,
844 icon:"interfaces/" + gs.xsltParams.interface_name + "/images/bluemarker.png"
845 });
846 }
847
848 var docElement = gs.jqGet("div" + doc.nodeID);
849 var parent;
850 if(docElement)
851 {
852 parent = docElement.parentNode;
853 }
854
855 while(parent && parent.nodeName != "BODY")
856 {
857 if($(parent).attr("id") && $(parent).attr("id").search("divCL") != -1)
858 {
859 doc.parentCL = parent;
860 break;
861 }
862
863 parent = parent.parentNode;
864 }
865
866 var info = new google.maps.InfoWindow({content:doc.title});
867 marker.markerInfo = info;
868 doc.marker = marker;
869 attachClickHandler(marker, doc.nodeID);
870}
871
872// TODO: with the following, it seems that clicking on shape expands the entire document
873// Should it be that clicking on a shape should expand the doc section that contains that shape meta?
874function attachClickHandler(shapeOrMarker, nodeID)
875{
876 google.maps.event.addListener(shapeOrMarker, 'click', function()
877 {
878 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";
879 });
880}
881
882function NewLatLng(lat, lng)
883{
884 console.log("Latitude " + lat);
885 console.log("Longitude " + lng);
886}
887
888function httpMapBrowseRequest(sectionID)
889{
890 // Make ajax call to retrieve jsonNodes for section ID, and draw shapes on the map.
891 var url = gs.xsltParams.library_name + "?a=b&rt=s&s=ClassifierBrowse&c=" + gs.cgiParams.c + "&cl=" + sectionID + "&excerptid=jsonNodes";
892 $.ajax(url)
893 .success(function(responseText)
894 {
895 var startIndex = responseText.indexOf(">");
896 var endIndex = responseText.indexOf("</");
897
898 var jsonNodesStr = responseText.substring(startIndex+1, endIndex);
899 var jsonNodes = eval(jsonNodesStr); //responseText.substring(startIndex+1, endIndex));
900 if(jsonNodes && jsonNodes.length > 0)
901 {
902
903 mapEnabled = true;
904 showMap("httpMapBrowseRequest");
905
906 for(var i = 0; i < jsonNodes.length; i++)
907 {
908 var doc = jsonNodes[i];
909 _docList[doc.nodeID] = doc;
910 _docList.ids.push(doc.nodeID);
911
912 var options = {
913 "mainDoc": true // TODO: should this be true or false???
914 };
915 createOverlayItems(doc, options);
916 }
917
918 ///var tmp = $("#title"+sectionID);
919 ///console.log(tmp); // WRONG
920 ///var tmp2 = document.getElementById("title"+sectionID);
921 ///console.log(tmp2); // RIGHT, WHY?
922
923 // create data-* attribute to store this sectionID's JSON on the section's div
924 //$("#title"+sectionID).attr("data-gps-map-json", "hello world"); // TODO: Doesn't work. Why?
925 var titleClassifierEl = document.getElementById("title"+sectionID);
926 titleClassifierEl.setAttribute("data-gps-map-json", jsonNodesStr);
927 }
928
929 updateMap();
930 //console.log("getSub Classifier -> updateMap()");
931 })
932 .error(function()
933 {
934 //console.log("Error getting subclassifiers");
935 return;
936 });
937}
938
939function performDistanceSearchWithCoordinates(id, coord, degrees)
940{
941 var coordInfo = getLatLngForCoord(coord);
942 if(!coordInfo) {
943 console.log("@@@ ERROR in map-scripts::performDistanceSearchWithCoordinates: coordInfo is null");
944 }
945 performDistanceSearch(id, coordInfo.lat, coordInfo.lng, degrees);
946}
947
948function performDistanceSearch(id, lat, lng, degrees)
949{
950 if(parseFloat(lat) > 180 || parseFloat(lat) < -180 || parseFloat(lng) > 180 || parseFloat(lat) < -180)
951 {
952 console.log("Latitude or longitude incorrectly formatted");
953 return;
954 }
955
956 if(lat.indexOf(".") == -1 || lng.indexOf(".") == -1 || (lat.indexOf(".") + 3) >= lat.length || (lng.indexOf(".") + 3) >= lng.length)
957 {
958 console.log("Latitude or longitude does not have the required precision for a distance search");
959 return;
960 }
961
962 var query = "";
963 for(var i = 0; i < degrees * 2; i++)
964 {
965 for (var j = 0; j < degrees * 2; j++)
966 {
967 var latDelta = (i - degrees) * 0.01;
968 var lngDelta = (j - degrees) * 0.01;
969
970 //query += "(" + getDistanceQueryStringOldApproach(lat, latDelta, 2, "LA", ["N","S"]);
971 //query += "+AND+";
972 //query += getDistanceQueryStringOldApproach(lng, lngDelta, 2, "LN", ["E","W"]) + ")";
973
974 query += "(" + getDistanceQueryStringTerm(lat, lng, latDelta, lngDelta, 2, "CS") + ")";
975
976 if(i != ((degrees * 2) - 1) || j != ((degrees * 2) - 1)){ query += "+OR+"; }
977 }
978 }
979
980 var inlineTemplate = '\
981 <xsl:template match="/" priority="5">\
982 <table id="nearbyDocs">\
983 <tr>\
984 <th><a href="javascript:sortByDistance();">Distance</a></th><th><a href="javascript:sortAlphabetically();">Document</a></th>\
985 </tr>\
986 <xsl:apply-templates select="//documentNode"/>\
987 </table>\
988 </xsl:template>\
989 \
990 <xsl:template match="documentNode" priority="5">\
991 <xsl:if test="@nodeID !=\''+id+'\'">\
992 <tr>\
993 <td>___<gsf:metadata name="Latitude"/>______<gsf:metadata name="Longitude"/>___</td>\
994 <td><gsf:link title="'+gs.text.doc.nearby_doc_tooltip+'" type="document"><gsf:metadata name="Title"/></gsf:link></td>\
995 </tr>\
996 </xsl:if>\
997 </xsl:template>';
998
999 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");
1000 $.ajax(url)
1001 .success(function(response)
1002 {
1003 response = response.replace(/<img src="[^"]*map_marker.png"[^>]*>/g, "");
1004
1005 var nearbyDocsArray = new Array();
1006
1007 var lats = new Array();
1008 var lngs = new Array();
1009 var matches = response.match(/___(-?[0-9\.]*)___/g);
1010 for(var i = 0; i < matches.length; i += 2)
1011 {
1012 var matchLatFloat = parseFloat(matches[i].replace("___", ""));
1013 var matchLngFloat = parseFloat(matches[i+1].replace("___", ""));
1014
1015 lats.push(matchLatFloat);
1016 lngs.push(matchLngFloat);
1017 var distance = Math.sqrt(Math.pow(matchLatFloat - parseFloat(lat), 2) + Math.pow(matchLngFloat - parseFloat(lng), 2)) * (40000.0/360.0);
1018 var distanceString = "" + distance;
1019 distanceString = distanceString.substring(0, 6);
1020 response = response.replace(matches[i] + matches[i+1], distanceString);
1021 }
1022
1023 var index = 0;
1024 var i = 0;
1025 while(true)
1026 {
1027 var distanceStart = response.indexOf("<td>", index);
1028 if(distanceStart == -1)
1029 {
1030 break;
1031 }
1032 var distanceEnd = response.indexOf("</td>", distanceStart);
1033
1034 var docLinkStart = response.indexOf("<td>", distanceEnd);
1035 var docLinkEnd = response.indexOf("</td>", docLinkStart);
1036
1037 var dist = response.substring(distanceStart + 4, distanceEnd);
1038 var docLink = response.substring(docLinkStart + 4, docLinkEnd);
1039
1040 _nearbyDocsByDistance.push({title:docLink, distance:dist, lat:lats[i], lng:lngs[i++]});
1041
1042 index = docLinkEnd;
1043 }
1044
1045 sortByDistance(lat,lng);
1046
1047 var toggle = $("#nearbyDocumentsToggle");
1048 toggle.attr("src", gs.imageURLs.collapse);
1049 gs.functions.makeToggle(toggle, $("#nearbyDocuments"));
1050 });
1051}
1052
1053var map_centering_timeout = null;
1054function recenterMapF(lat, lng)
1055{
1056 return function() {
1057 _map.setCenter(new google.maps.LatLng(lat, lng));
1058 }
1059}
1060function recenterMap(lat, lng)
1061{
1062
1063 _map.setCenter(new google.maps.LatLng(lat, lng));
1064
1065}
1066function sortByDistance(base_lat,base_lng)
1067{
1068 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>';
1069 _nearbyDocsByDistance.sort(function(a, b){return (a.distance - b.distance);});
1070 for(var i = 0; i < _nearbyDocsByDistance.length; i++)
1071 {
1072
1073 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>";
1074 }
1075 sortedTable += "</table>";
1076
1077 $("#nearbyDocuments").html(sortedTable);
1078}
1079function prettifyDistance(distance) {
1080
1081 var new_distance;
1082 if (distance < 1) {
1083 new_distance = (distance * 1000);
1084 // Round to nearest whole number - don't need to show points of metres..
1085 new_distance = Math.round(new_distance);
1086 new_distance += " m";
1087 }
1088 else {
1089 new_distance = distance +" km";
1090
1091 }
1092 return new_distance;
1093}
1094
1095
1096
1097function sortAlphabetically(base_lat, base_lng)
1098{
1099 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>';
1100 _nearbyDocsByDistance.sort(function(a, b)
1101 {
1102 var firstTitleStartIndex = a.title.indexOf(">");
1103 var firstTitleEndIndex = a.title.indexOf("<", firstTitleStartIndex);
1104 var firstTitle = a.title.substring(firstTitleStartIndex + 1, firstTitleEndIndex);
1105 var secondTitleStartIndex = b.title.indexOf(">");
1106 var secondTitleEndIndex = b.title.indexOf("<", secondTitleStartIndex);
1107 var secondTitle = b.title.substring(secondTitleStartIndex + 1, secondTitleEndIndex);
1108 return ((firstTitle.toLowerCase() == secondTitle.toLowerCase()) ? 0 : ((firstTitle.toLowerCase() > secondTitle.toLowerCase()) ? 1 : -1));
1109 });
1110 for(var i = 0; i < _nearbyDocsByDistance.length; i++)
1111 {
1112 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>";
1113 }
1114 sortedTable += "</table>";
1115
1116 $("#nearbyDocuments").html(sortedTable);
1117}
1118
1119function getDistanceQueryStringOldApproach(currentCoord, delta, precision, indexName, directions)
1120{
1121 console.error("**** Old Approach called!!!");
1122
1123 var query = "";
1124 var coordFloat = parseFloat(currentCoord);
1125
1126 var newCoord = "" + (coordFloat + delta);
1127 var beforeDec = newCoord.substring(0, newCoord.indexOf("."));
1128
1129 var direction = directions[0];
1130 if(coordFloat < 0)
1131 {
1132 // negative value
1133 direction = directions[1];
1134 beforeDec = beforeDec.substring(1); // skip over '-' at front
1135 }
1136 beforeDec += direction;
1137
1138 var afterDec = newCoord.substring(newCoord.indexOf(".") + 1, newCoord.indexOf(".") + (precision) + 1);
1139
1140 return indexName + ":" + beforeDec + "+AND+" + indexName + ":" + afterDec;
1141}
1142
1143function coordValToIndexToken(coordValStr, delta, precision, directions)
1144{
1145 var coordValFloat = parseFloat(coordValStr);
1146
1147 var deltaCoordValStr = "" + (coordValFloat + delta);
1148 var beforeDec = deltaCoordValStr.substring(0, deltaCoordValStr.indexOf("."));
1149
1150 var direction = directions[0];
1151 if(coordValFloat < 0)
1152 {
1153 // negative value
1154 direction = directions[1];
1155 beforeDec = beforeDec.substring(1); // skip over '-' at front
1156 }
1157
1158 var beforeDecWithDirection = beforeDec + direction;
1159
1160 var afterDecPrecision = deltaCoordValStr.substring(deltaCoordValStr.indexOf(".") + 1, deltaCoordValStr.indexOf(".") + (precision) + 1);
1161
1162 var indexToken = beforeDecWithDirection + afterDecPrecision;
1163
1164 return indexToken;
1165}
1166
1167function getDistanceQueryStringTerm(currentLat,currentLng, deltaLat, deltaLng, precision, indexName)
1168{
1169 var latToken = coordValToIndexToken(currentLat,deltaLat,precision,["N","S"]);
1170 var lngToken = coordValToIndexToken(currentLng,deltaLng,precision,["E","W"]);
1171
1172 var queryStringTerm = indexName + ":\"" + latToken + " " + lngToken + "\"";
1173
1174 return queryStringTerm;
1175}
1176
1177function _gsInfo(message, optObject) {
1178 console.log(message);
1179 if(typeof optObject !== 'undefined') {
1180 console.log(optObject);
1181 }
1182}
1183
1184function _gsDebug(message, optObject) {
1185 if(_DEBUGGING_) {
1186 _gsInfo(message, optObject);
1187 }
1188}
1189
1190
1191function _debugPrintDocList() {
1192 if(!_DEBUGGING_) return;
1193
1194 console.log("@@@@ printing docList:");
1195 for(var i = 0; i < _docList.ids.length; i++)
1196 {
1197 var doc = _docList.getDocByIndex(i);
1198 console.log(" At index = " + i + " ids[i]: " + _docList.ids[i]);
1199 console.log(" At index = " + i + " ids[i]: " + _docList.ids[i] + ", doc: " + doc.nodeID);
1200 }
1201}
1202
1203function _debugPrintBounds(bounds, message) {
1204 if(!_DEBUGGING_) return;
1205 var ne = bounds.getNorthEast();
1206 var sw = bounds.getSouthWest();
1207 console.log(message + " bounds: ne = " + ne + ", sw = " + sw);
1208}
Note: See TracBrowser for help on using the repository browser.