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

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

Fixing up error caused by debug print statement: no coord for marker.

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