Project

General

Profile

Download (54.4 KB) Statistics
| Branch: | Tag: | Revision:
1
//see also https://github.com/geetarista/jquery-plugin-template/blob/master/jquery.plugin-template.js
2

    
3
/**
4
 * Expected dom structure:
5
 *  '<div class="ahah-content" rel="'.$cdm_proxy_url.'"><span class="loading">Loading ....</span></div>';
6
 */
7
(function($, document, window, undefined) {
8

    
9
    $.fn.cdm_openlayers_map = function(mapserverBaseUrl, mapserverVersion, options) {
10

    
11
      var opts = $.extend({},$.fn.cdm_openlayers_map.defaults, options);
12

    
13
      // sanitize invalid opts.boundingBox
14
      if(opts.boundingBox &&  !( typeof opts.boundingBox  == 'string' && opts.boundingBox .length > 6)) {
15
        opts.boundingBox = null;
16
      }
17

    
18
      return this.each(function(){
19
          this.cdmOpenlayersMap = new CdmOpenLayers.Map($(this), mapserverBaseUrl, mapserverVersion, opts);
20
          this.cdmOpenlayersMap.create();
21
      }); // END each
22

    
23
    }; // END cdm_openlayers_map
24

    
25
})(jQuery, document, window, undefined);
26

    
27
(function($){
28
  $.fn.cdm_openlayers_map.defaults = {  // set up default options
29
    legendPosition:  null,      // 1,2,3,4,5,6 = display a legend in the corner specified by the number
30
    distributionOpacity: 0.75,
31
    legendOpacity: 0.75,
32
    // These are bounds in the epsg_4326 projection in degree
33
    boundingBox: null,
34
    aspectRatio: 2, // w/h
35
    showLayerSwitcher: false,
36
    baseLayerNames: ["open_topomap"],
37
    defaultBaseLayerName: 'open_topomap',
38
    maxZoom: 15,
39
    minZoom: 0,
40
    // hide the map when the data layer has no features
41
    hideEmptyMap: true,
42
    debug: true,
43
    layerLoadingTimeout: 1000, // ms
44
    /**
45
     * allows the map to display parts of the layers which are outside
46
     * the maxExtent if the aspect ratio of the map and of the baselayer
47
     * are not equal
48
     */
49
    displayOutsideMaxExtent: false,
50
    //  customWMSBaseLayerData: {
51
    //  name: "Euro+Med",
52
    //  url: "http://edit.africamuseum.be/geoserver/topp/wms",
53
    //  params: {layers: "topp:em_tiny_jan2003", format:"image/png", tiled: true},
54
    //  projection: "EPSG:7777777",
55
    //  maxExtent: "-1600072.75, -1800000, 5600000, 5850093",
56
    //  units: 'm'
57
    //  }
58
    customWMSBaseLayerData: {
59
        name: null,
60
        url: null,
61
        params: null,
62
        projection: null,
63
        proj4js_def: null,
64
        max_extent: null,
65
        units: null,
66
        untiled: null
67
    },
68
    wmsOverlayLayerData: {
69
      name: null,
70
      url: null,
71
      params: null,
72
      untiled: null
73
    },
74
    /**
75
     * when true the map is made resizable by adding the jQueryUI widget resizable
76
     * to the map container. This feature requires that the jQueryUI is loaded
77
     */
78
    resizable: false,
79
    wfsRootUrl: 'http://edit.africamuseum.be/geoserver/topp/ows',
80
    specimenPageBaseUrl: '/cdm_dataportal/occurrence/',
81
      specimenLinkText: 'Open unit'
82
  };
83
})(jQuery);
84

    
85

    
86
/**************************************************************************
87
 *                          CdmOpenLayers
88
 **************************************************************************/
89
(function() {
90

    
91
    /**
92
     * The CdmOpenLayers namespace definition
93
     */
94
    window.CdmOpenLayers  = (function () {
95

    
96
        // EPSG:3857 from http://spatialreference.org/ref/sr-org/6864/proj4/
97
        // OpenStreetMap etc
98
        Proj4js.defs["EPSG:3857"] = '+proj=merc +lon_0=0 +k=1 +x_0=0 +y_0=0 +a=6378137 +b=6378137 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs';
99
        Proj4js.defs["EPSG:7777777"] = '+proj=lcc +lat_1=42 +lat_2=56 +lat_0=35 +lon_0=24 +x_0=3000000 +y_0=100000 +ellps=intl +towgs84=-87,-98,-121,0,0,0,0 +units=m +no_defs';
100

    
101
        var projections = {
102
                epsg_4326: new OpenLayers.Projection("EPSG:4326"),
103
                epsg_900913: new OpenLayers.Projection("EPSG:900913"),
104
                epsg_3857:  new OpenLayers.Projection("EPSG:3857"),
105
                epsg_7777777:  new OpenLayers.Projection("EPSG:7777777")
106
        };
107
        var mapExtends = {
108
                epsg_4326: new OpenLayers.Bounds(-180, -90, 180, 90),
109
                //  Spherical Mercator epsg_900913 is not supporting the whole marble
110
                epsg_900913: new OpenLayers.Bounds(-179, -85, 179, 85),
111
                //  Spherical Mercator
112
                epsg_3857: new OpenLayers.Bounds(-179, -85, 179, 85)
113
        };
114
        // transform epsg_900913 to units meter
115
        mapExtends.epsg_900913.transform(projections.epsg_4326, projections.epsg_900913);
116
        mapExtends.epsg_3857.transform(projections.epsg_4326, projections.epsg_3857);
117

    
118
        // make public by returning an object
119
        return {
120
            projections: projections,
121
            mapExtends: mapExtends,
122
            getMap: function (){},
123
            getLayerByName: function(layerName){} // initially empty function, will be populated by openlayers_layers.js
124
        };
125

    
126
    })(); // end of namespace definition for CdmOpenLayers
127

    
128
    /**
129
     * The CdmOpenLayers.Map constructor
130
     * @param mapElement
131
     * @param mapserverBaseUrl
132
     * @param mapserverVersion
133
     * @param opts
134
     * @returns
135
     */
136
    window.CdmOpenLayers.Map = function(mapElement, mapserverBaseUrl, mapserverVersion, opts){
137

    
138
      var mapServicePath = '/edit_wp5';
139

    
140
      // firebug console stub (avoids errors if firebug is not active)
141
      if(typeof console === "undefined") {
142
          console = { log: function() { } };
143
      }
144

    
145
      // sanitize given options
146
      try {
147
          opts.customWMSBaseLayerData.max_extent = OpenLayers.Bounds.fromString(opts.customWMSBaseLayerData.max_extent);
148
      } catch(e){
149
          opts.customWMSBaseLayerData.max_extent = null;
150
      }
151

    
152

    
153
      var legendImgSrc = null;
154

    
155
      var map = null;
156

    
157
      var infoElement = null;
158

    
159
      var baseLayers = [];
160

    
161
      var defaultBaseLayer = null;
162

    
163
      /**
164
       * The top most layer which will be places above all data layers
165
       *
166
       * @type {null}
167
       */
168
      var wmsOverlay = null;
169

    
170
        /**
171
         * The Control element to handle clicks on features in KML layers
172
         * @type {null}
173
         */
174
      var kmlSelectControl = null;
175

    
176
      /**
177
       * Default bounding box for map viewport in the projection of the base layer.
178
       * as defined by the user, can be null.
179
       *
180
       * These are bounds in the epsg_4326 projection, and will be transformed to the baselayer projection.
181
       *
182
       * @type string
183
       */
184
      var defaultBaseLayerBoundingBox = "-180,-90,180,90";
185

    
186
      /**
187
       * bounding box for map viewport as defined by the user, can be null.
188
       *
189
       * These are bounds in the projection of the base layer.
190
       *
191
       * @type string
192
       */
193
      var boundingBox = null;
194

    
195
      /**
196
       * Bounds for the view port calculated from the data layer responses.
197
       * These are either calculated by the minimum bounding box which
198
       * encloses the data in the data layers, or it is equal to the
199
       * boundingBox as defined by the user.
200
       *
201
       * These are bounds in the projection of the base layer.
202
       *
203
       * @see boundingBox
204
       *
205
       * @type OpenLayers.Bounds
206
       */
207
      var dataBounds = null;
208

    
209
      /**
210
       * Final value for the view port, calculated from the other bounds.
211
       *
212
       * These are bounds in the projection of the base layer.
213
       *
214
       * @type OpenLayers.Bounds
215
       */
216
      var zoomToBounds = null;
217

    
218
        /**
219
         * This as been introduced for #5683 with value TRUE,
220
         * but can cause feature to tbe hidden in KML layers.
221
         * Therefore this is now set to false.
222
         *
223
         * @type {boolean}
224
         */
225
      var zoomToClosestLevel = false;
226

    
227
      var LAYER_DATA_CNT = 0;
228

    
229
      /* this is usually the <div id="openlayers"> element */
230
      var mapContainerElement = mapElement.parent();
231

    
232
      var errorMessageCtl;
233
      var defaultControls = [
234
         new OpenLayers.Control.PanZoom(),
235
         new OpenLayers.Control.Navigation(
236
           {
237
             zoomWheelEnabled: false,
238
             handleRightClicks:true,
239
             zoomBoxKeyMask: OpenLayers.Handler.MOD_CTRL
240
           }
241
         )
242
      ];
243

    
244
      var layerByNameMap = {
245
              tdwg1: 'topp:tdwg_level_1',
246
              tdwg2: 'topp:tdwg_level_2',
247
              tdwg3: 'topp:tdwg_level_3',
248
              tdwg4: 'topp:tdwg_level_4'
249
      };
250

    
251
      /**
252
       * Known projections by layer name. This map helps avoid requesting the server for the
253
       * projection. See readProjection()
254
       */
255
      var layerProjections = {
256
        'topp:tdwg_level_1': CdmOpenLayers.projections.epsg_4326,
257
        'topp:tdwg_level_2': CdmOpenLayers.projections.epsg_4326,
258
        'topp:tdwg_level_3': CdmOpenLayers.projections.epsg_4326,
259
        'topp:tdwg_level_4': CdmOpenLayers.projections.epsg_4326,
260
        'topp:phytogeographical_regions_of_greece': CdmOpenLayers.projections.epsg_4326,
261
        'topp:euromed_2013': CdmOpenLayers.projections.epsg_7777777,
262
        'topp:flora_cuba_2016': CdmOpenLayers.projections.epsg_4326
263
      };
264

    
265
      if(opts.resizable === true) {
266
        // resizable requires jQueryUI to  be loaded!!!
267
        mapContainerElement.resizable({
268
          resize: function( event, ui ) {
269
            map.updateSize();
270
            //   this.printInfo();
271
          }
272
        });
273
      }
274

    
275
        /**
276
         * Removes the map and the parent container from the
277
         * DOM and destroys the OpenLayers map object
278
         */
279
        function removeMap() {
280
            //  if you are using an application which removes a container
281
            //  of the map from the DOM, you need to ensure that you destroy the map before this happens;
282
            map.destroy;
283
            jQuery(map.div).parent().remove();
284
        }
285

    
286
        function reportAjaxError(textStatus, requestUrl, errorThrown) {
287
            if (!textStatus) {
288
                textStatus = "error";
289
            }
290
            var errorMessage = errorThrown != undefined ? errorThrown : 'unspecified error';
291
            log(textStatus + " requesting  " + requestUrl + " failed due to " + errorMessage, true);
292
            errorMessageCtl.show();
293
            errorMessageCtl.addErrorMessage(textStatus + ":" + errorThrown);
294
        }
295

    
296
        function reportWMSError(layer) {
297
            log('Loading of WMS layer ' + layer.name + ' failed.', true);
298
            errorMessageCtl.show();
299
            errorMessageCtl.addErrorMessage('Loading of WMS layer ' + layer.name + ' failed');
300
        }
301

    
302
        /**
303
         *
304
         */
305
        this.create = function(){ // public function
306

    
307
          // set the height of the container element
308
          adjustHeight();
309

    
310
          // register for resize events to be able to adjust the map aspect ratio and legend position
311
          jQuery( window ).resize(function() {
312
            adjustHeight();
313
            adjustLegendAsElementPosition();
314
          });
315

    
316
          createBaseLayers(opts.baseLayerNames, opts.defaultBaseLayerName, opts.customWMSBaseLayerData);
317

    
318
          initMap();
319

    
320
          var boundingBoxEPSG4326 = null;
321
          if(opts.boundingBox){
322
            boundingBox = OpenLayers.Bounds.fromString(opts.boundingBox);
323
            // no need to use readProjection(), for base layers the projection should always be in the layer data
324
            boundingBoxEPSG4326 = boundingBox.transform(baseLayers[0].projection, CdmOpenLayers.projections.epsg_4326);
325
          }
326

    
327
          // -- Distribution Layer --
328
          var mapServiceRequest;
329
          var distributionQuery = mapElement.attr('data-distributionQuery');
330

    
331
          if(distributionQuery !== undefined){
332
            distributionQuery = mergeQueryStrings(distributionQuery, '&recalculate=false');
333
            if(typeof legendPosition === 'number'){
334
              distributionQuery = mergeQueryStrings(distributionQuery, 'legend=1&mlp=' + opts.legendPosition);
335
            }
336
            if(boundingBoxEPSG4326){
337
              distributionQuery = mergeQueryStrings(distributionQuery, 'bbox=' + boundingBoxEPSG4326);
338
            }
339

    
340
            // distributionQuery = mergeQueryStrings(distributionQuery, 'callback=?');
341
            var legendFormatQuery = mapElement.attr('data-legendFormatQuery');
342
            if(legendFormatQuery !== undefined){
343
              legendImgSrc = mergeQueryStrings('/wms?SERVICE=WMS&VERSION=1.1.1&REQUEST=GetLegendGraphic ', legendFormatQuery);
344
            }
345

    
346
            mapServiceRequest = mapserverBaseUrl + mapServicePath + '/' + mapserverVersion + '/rest_gen.php?' + distributionQuery;
347
            log("area data map service request:" + mapServiceRequest);
348

    
349
            LAYER_DATA_CNT++;
350
            jQuery.ajax({
351
              url: mapServiceRequest,
352
              dataType: "json",
353
                // timeout: layerLoadingTimeout,
354
              success: function(data){
355
                  var layers = createDataLayers(data, "AREA");
356
                  addLayers(layers);
357
                // layerDataLoaded(); will be called after reading the projection from the WFS
358
                // for the data layer, see readProjection()
359
              },
360
              error: function(jqXHR, textStatus, errorThrown){
361
                  reportAjaxError("Distribution Layer: " +textStatus, mapServiceRequest, errorThrown);
362
              }
363
            });
364
          }
365

    
366
          // -- Occurrence Layer --
367
          var occurrenceQuery = mapElement.attr('data-occurrenceQuery');
368
          if(occurrenceQuery !== undefined){
369
            occurrenceQuery = mergeQueryStrings(occurrenceQuery, '&recalculate=false');
370
//              if(typeof legendPosition == 'number'){
371
//              occurrenceQuery = mergeQueryStrings(distributionQuery, 'legend=1&mlp=' + opts.legendPosition);
372
//              }
373

    
374

    
375
            occurrenceQuery = mergeQueryStrings(occurrenceQuery, 'callback=?');
376
//              var legendFormatQuery = mapElement.attr('legendFormatQuery');
377
//              if(legendFormatQuery !== undefined){
378
//              legendImgSrc = mergeQueryStrings('/GetLegendGraphic?SERVICE=WMS&VERSION=1.1.1', legendFormatQuery);
379
//              }
380
            if(boundingBoxEPSG4326){
381
              occurrenceQuery = mergeQueryStrings(occurrenceQuery, 'bbox=' + boundingBoxEPSG4326);
382
            }
383

    
384
            mapServiceRequest = mapserverBaseUrl + mapServicePath + '/' + mapserverVersion + '/rest_gen.php?' + occurrenceQuery;
385
            log("Point data map service request:" + mapServiceRequest);
386

    
387
            LAYER_DATA_CNT++;
388
            jQuery.ajax({
389
              url: mapServiceRequest,
390
              dataType: "json",
391
              success: function(data){
392
                  var layers = createDataLayers(data, "POINT");
393
                  addLayers(layers);
394
                  // layerDataLoaded(); will be called after reading the projection from the WFS for the data layer
395
              },
396
                error: function(jqXHR, textStatus, errorThrown){
397
                    reportAjaxError("Occurrence Layer: " + textStatus, mapServiceRequest, errorThrown);
398
                }
399
            });
400
          }
401

    
402
            // -- KML Layer --
403
            var kmlRequestUrl = mapElement.attr('data-kml-request-url');
404
            if(kmlRequestUrl !== undefined){
405

    
406
                LAYER_DATA_CNT++;
407
                var kmlLayer = new OpenLayers.Layer.Vector("KML", {
408
                    strategies: [new OpenLayers.Strategy.Fixed()],
409
                    protocol: new OpenLayers.Protocol.HTTP({
410
                        url: kmlRequestUrl,
411
                        format: new OpenLayers.Format.KML({
412
                            extractStyles: true,
413
                            extractAttributes: true
414
                            // maxDepth: 2
415
                        })
416
                    })
417
                });
418
                map.addLayer(kmlLayer);
419
                // create select control
420
                kmlSelectControl = new OpenLayers.Control.SelectFeature(kmlLayer);
421
                kmlLayer.events.on({
422
                    "featureselected": onKmlFeatureSelect,
423
                    "featureunselected": onKmlFeatureUnselect,
424
                    "reportError": true,
425
                    'loadend': function(event) {
426
                        if(opts.hideEmptyMap && kmlLayer.features.length == 0){
427
                            log("No feature in KML layer, removing map ...")
428
                            removeMap();
429
                        } else {
430
                            applyLayerZoomBounds(event);
431
                            disablePolygonFeatureClick(event);
432
                        }
433
                    }
434
                });
435
                map.addControl(kmlSelectControl);
436
                kmlSelectControl.activate();
437

    
438
                initPostDataLoaded();
439
            }
440

    
441
          if(LAYER_DATA_CNT === 0) {
442
            // a map only with base layer
443
            initPostDataLoaded();
444
          }
445

    
446
          // -- Overlay Layer --
447
          if(opts.wmsOverlayLayerData.params){
448
            overlay_layer_params = opts.wmsOverlayLayerData.params;
449
            overlay_layer_params.transparent=true;
450
            wmsOverlay = createWMSLayer(
451
              opts.wmsOverlayLayerData.name,
452
              opts.wmsOverlayLayerData.url,
453
              overlay_layer_params,
454
              null,
455
              null,
456
              null,
457
              null,
458
              opts.wmsOverlayLayerData.untiled
459
            );
460

    
461
            if(map.addLayer(wmsOverlay)){
462
              wmsOverlay.setVisibility(true);
463
              map.setLayerIndex(wmsOverlay, 100);
464
              log("Overlay wms added");
465
            } else {
466
              log("ERROR adding overlay wms layer")
467
            }
468
          }
469

    
470
          log("Map viewer creation complete.");
471

    
472
        };
473

    
474
      /**
475
       * Provides the layer name which can be used in WMS/WFS requests.
476
       * The layerData.tdwg field contains invalid layer names in case of
477
       * the tdwg layers. This function handles with this bug.
478
       *
479
       * @param layerData
480
       * @returns String
481
       *    the correct layer name
482
       */
483
        var fixLayerName = function(layerData){
484
         var wmsLayerName = layerByNameMap[layerData.tdwg];
485
         if(!wmsLayerName){
486
           wmsLayerName = "topp:" + layerData.tdwg;
487
         }
488
         return wmsLayerName;
489
        };
490

    
491
        var layerDataLoaded = function() {
492
          LAYER_DATA_CNT--;
493
          if(LAYER_DATA_CNT === 0){
494
            initPostDataLoaded();
495
          }
496
        };
497

    
498
        var initPostDataLoaded = function () {
499
          // all layers prepared, make them visible
500
          map.layers.forEach(function(layer){
501
            layer.setVisibility(true);
502
          });
503

    
504
          // zoom to the zoomToBounds
505
          log(" > starting zoomToExtend: " + zoomToBounds + ", zoomToClosestLevel: " + zoomToClosestLevel, true);
506
          map.zoomToExtent(zoomToBounds, zoomToClosestLevel);
507

    
508

    
509
          if(map.getZoom() > opts.maxZoom){
510
            map.zoomTo(opts.maxZoom);
511
          } else if(map.getZoom() < opts.minZoom){
512
            map.zoomTo(opts.minZoom);
513
          }
514

    
515
          // make sure the wmsOverlay is still on top
516
          if(wmsOverlay){
517
            map.setLayerIndex(wmsOverlay, 100);
518
          }
519

    
520
          log(" > zoomToExtend done", true);
521
        };
522

    
523
      /**
524
       * Returns  the projection of the defaultBaseLayer which is the
525
       * the projection to which all other layers and locations must be transformed.
526
       */
527
      var referenceProjection = function() {
528
        if(defaultBaseLayer){
529
          if(defaultBaseLayer.projection){
530
            return defaultBaseLayer.projection;
531
          } else if(defaultBaseLayer.sphericalMercator === true){
532
            return CdmOpenLayers.projections.epsg_900913;
533
          } else {
534
            log("Error - referenceProjection() defaultBaseLayer " + defaultBaseLayer.name + " misses projection information");
535
          }
536

    
537
        } else {
538
          log("Error - referenceProjection() defaultBaseLayer not set");
539
          return null;
540
        }
541
      };
542

    
543
      /**
544
       * Returns the maxExtent of the defaultBaseLayer.
545
       */
546
      var referenceMaxExtent = function() {
547
        if(defaultBaseLayer){
548
          return defaultBaseLayer.maxExtent;
549
        } else {
550
          log("Error - referenceMaxExtent() defaultBaseLayer not set");
551
          return null;
552
        }
553
      };
554

    
555
        var getHeight = function(){
556
          return mapContainerElement.width() / opts.aspectRatio;
557
        };
558

    
559
        var getWidth = function(){
560
          return mapContainerElement.width();
561
        };
562

    
563
        var adjustHeight = function() {
564
          mapContainerElement.css("height", getHeight());
565
        };
566

    
567
        /**
568
         * public function
569
         */
570
        this.registerEvents = function(events){
571
            for (var key in events) {
572
                if (events.hasOwnProperty(key)) {
573
                    map.events.register(key, map , events[key]);
574
                }
575
            }
576
        };
577

    
578

    
579
        /**
580
         * public function
581
         */
582
        this.getMap = function(){
583
            return map;
584
        };
585

    
586
        /**
587
         * Prints info on the current map into the jQuery element
588
         * as set in the options (opts.infoElement)
589
         * public function
590
         *
591
         */
592
        this.printInfo = function(){
593

    
594

    
595
            var mapExtendDegree = null;
596
            if(map.getExtent() != null){
597
              // If the baselayer is not yet set, getExtent() returns null.
598
              mapExtendDegree = map.getExtent().clone();
599
              mapExtendDegree.transform(map.baseLayer.projection, CdmOpenLayers.projections.epsg_4326);
600
            }
601

    
602
            var info = "<dl>";
603
            info += "<dt>zoom:<dt><dd>" + map.getZoom() + "</dd>";
604
            if(opts.debug){
605
                info += "<dt>map resolution:<dt><dd>" + map.getResolution() + "</dd>";
606
                info += "<dt>map max resolution:<dt><dd>" + map.getMaxResolution() + "</dd>";
607
                info += "<dt>map scale:<dt><dd>" + map.getScale() + "</dd>";
608
                info += "<dt>map width, height:<dt><dd>" + mapContainerElement.width() +  ", " + mapContainerElement.height() + "</dd>";
609
                info += "<dt>map aspect ratio:<dt><dd>" + mapContainerElement.width() / mapContainerElement.height() + "</dd>";
610
                if(map.getExtent() != null){
611
                  info += "<dt>map extent bbox:<dt><dd class=\"map-extent-bbox\"><span class=\"layer-value\">" + map.getExtent().toBBOX() + "</span>, (in degree:<span class=\"degree-value\">" + mapExtendDegree.toBBOX() + "</span>)</dd>";
612
                  info += "<dt>map maxExtent bbox:<dt><dd>" + map.getMaxExtent().toBBOX() + "</dd>";
613
                  info += "<dt>baselayer extent bbox:<dt><dd class=\"baselayer-extent-bbox\"><span class=\"layer-value\">" +  map.baseLayer.getExtent().toBBOX() + "</span>, (in degree: <span class=\"degree-value\">"
614
                    + map.baseLayer.getExtent().clone().transform(map.baseLayer.projection, CdmOpenLayers.projections.epsg_4326) + "</span>)</dd>";
615
                  info += "<dt>baselayer projection:<dt><dd>" + map.baseLayer.projection.getCode() + "</dd>";
616
                }
617
            } else {
618
                info += "<dt>bbox:<dt><dd>" + (mapExtendDegree !== null ? mapExtendDegree.toBBOX() : 'NULL') + "</dd>";
619
            }
620
            info += "</dl>";
621

    
622
            if(infoElement === null){
623
                infoElement = jQuery('<div class="map_info"></div>');
624
                mapElement.parent().after(infoElement);
625
            }
626
            infoElement.html(info);
627
        };
628

    
629
        /**
630
         * Initialize the Openlayers Map with the base layer
631
         */
632
        var initMap = function(){
633

    
634
          if(opts.showLayerSwitcher === true){
635
              defaultControls.push(new OpenLayers.Control.LayerSwitcher({'ascending':false}));
636
          }
637
          errorMessageCtl = errorMessageControl();
638
          errorMessageCtl.deactivate(); // initially inactive
639
          defaultControls.push(errorMessageCtl);
640

    
641
          defaultControls.unshift(layerLoadingControl()); // as first control, needs to be below all others!
642

    
643

    
644
//          var maxExtentByAspectRatio = cropBoundsToAspectRatio(defaultBaseLayer.maxExtent, getWidth/getHeight);
645
          var maxResolution = null;
646
          // gmaps has no maxExtent at this point, need to check for null
647
          if(referenceMaxExtent() !== null){
648
              maxResolution = Math[(opts.displayOutsideMaxExtent ? 'max' : 'min')](
649
                referenceMaxExtent().getWidth() / getWidth(),
650
                referenceMaxExtent().getHeight() / getHeight()
651
              );
652
          }
653
          console.log("mapOptions.maxResolution: " + maxResolution);
654
          console.log("mapOptions.restrictedExtent: " + referenceMaxExtent());
655

    
656
          map = new OpenLayers.Map(
657
            mapElement.attr('id'),
658
            {
659
              // defines the map ui elements and interaction features
660
              controls: defaultControls,
661

    
662
              // maxResolution determines the lowest zoom level and thus places the map
663
              // in its maximum extent into the available view port so that no additional
664
              // gutter is visible and no parts of the map are hidden
665
              // see http://trac.osgeo.org/openlayers/wiki/SettingZoomLevels
666
              // IMPORTANT!!!
667
              // the maxResulution set here will be overwritten if the baselayers maxResolution
668
              // it is set
669
              maxResolution: maxResolution,
670

    
671
              // setting restrictedExtent the the maxExtent prevents from panning the
672
              // map out of its bounds
673
              restrictedExtent: referenceMaxExtent(),
674
//                      maxExtent: referenceMaxExtent(),
675

    
676
              // Setting the map.fractionalZoom property to true allows zooming to an arbitrary level
677
              // (between the min and max resolutions).
678
              // fractional tiles are not supported by XYZ layers like OSM so this option would
679
              // break the tile retrieval for OSM (e.g.: tile for fractional zoom level
680
              // 1.2933333333333332 = http://b.tile.openstreetmap.org/1.2933333333333332/1/0.png)
681
              fractionalZoom: defaultBaseLayer.CLASS_NAME != "OpenLayers.Layer.OSM" && defaultBaseLayer.CLASS_NAME != "OpenLayers.Layer.XYZ",
682

    
683
              eventListeners: opts.eventListeners,
684
              // creating the map with a null theme, since we include the stylesheet directly in the page
685
              theme: null
686

    
687
            }
688
          );
689

    
690
          //add the base layers
691

    
692
          addLayers(baseLayers);
693
          map.setBaseLayer(defaultBaseLayer);
694

    
695
          // calculate the bounds to zoom to
696
          zoomToBounds = calculateZoomToBounds(opts.boundingBox ? opts.boundingBox : defaultBaseLayerBoundingBox);
697
          // zoomToBounds = cropBoundsToAspectRatio(zoomToBounds, map.getSize().w / map.getSize().h);
698
          console.log("baselayer zoomToBounds: " + zoomToBounds);
699

    
700
        };
701

    
702
        var addLayers = function(layers){
703

    
704
          layers.forEach(function(layer){
705
            // layer.setVisibility(false);
706
          });
707
          map.addLayers(layers);
708
        };
709

    
710
        /**
711
         * add a distribution or occurrence layer
712
         *
713
         * @param mapResponseArray
714
         *   The map service returns the mapResponseObj in an array with one element.
715
         * @param dataType
716
         *   either "AREA" or "POINT"
717
         */
718
        var createDataLayers = function(mapResponseArray, dataType){
719

    
720
          console.log("createDataLayers() : creating data layer of type " + dataType);
721

    
722
          var dataLayerOptions = makeWMSLayerOptions();
723
          dataLayerOptions.displayOutsideMaxExtent = true; // move into makeWMSLayerOptions?
724

    
725
          var layers = [];
726
          // add additional layers, get them from the mapResponseObj
727
          if(mapResponseArray !== undefined){
728
              var mapResponseObj = mapResponseArray[0];
729
             // ----------- POINT  -----------
730
            if(dataType === "POINT" && mapResponseObj.points_sld !== undefined){
731
              var pointLayer;
732
              // it is a response for an point map
733
              var geoserverUri;
734
              if(mapResponseObj.geoserver) {
735
                  geoserverUri = mapResponseObj.geoserver;
736
              } else {
737
                  // it is an old service which is not providing the corresponding geoserver URI, so we guess it
738
                  geoserverUri = mapserverBaseUrl + "/geoserver/wms";
739
              }
740

    
741
              //TODO points_sld should be renamed to sld in response + fill path to sld should be given
742
              pointLayer = new OpenLayers.Layer.WMS(
743
                'points',
744
                geoserverUri,
745
                {
746
                    layers: 'topp:rest_points',
747
                    transparent:"true",
748
                    format:"image/png"
749
                },
750
                dataLayerOptions
751
              );
752

    
753
              var sld = mapResponseObj.points_sld;
754
              if(sld.indexOf("http://") !== 0){
755
                  // it is an old servive which is not providing the full sdl URI, so we guess it
756
                  //  http://edit.africamuseum.be/synthesys/www/v1/sld/
757
                  //  http://edit.br.fgov.be/synthesys/www/v1/sld/
758
                  sld =  mapserverBaseUrl + "/synthesys/www/v1/sld/" + sld;
759
              }
760
              pointLayer.params.SLD = sld;
761

    
762
              layers.push(pointLayer);
763
            } else {
764
                // ----------- AREA  -----------
765
                // it is a response from for a distribution map
766
              for ( var i in mapResponseObj.layers) {
767
                var layerData = mapResponseObj.layers[i];
768
                var layerName = fixLayerName(layerData);
769
                console.log(" " + i +" -> " + layerName);
770
                var layer = new OpenLayers.Layer.WMS(
771
                  layerName,
772
                  mapResponseObj.geoserver,
773
                  {
774
                      layers: fixLayerName(layerData),
775
                      transparent:"true",
776
                      format:"image/png"
777
                  },
778
                  dataLayerOptions
779
                  );
780
                layer.params.SLD = layerData.sld;
781
                layer.setOpacity(opts.distributionOpacity);
782
                layers.push(layer);
783
              }
784
            }
785

    
786
            if(layers.length > 0) {
787
              // calculate zoomBounds using the first layer
788
              if(mapResponseObj.bbox !== undefined){
789
                var newBounds =  OpenLayers.Bounds.fromString( mapResponseObj.bbox);
790
                var projection;
791
                if(dataType === "POINT" || dataType === "AREA"){
792
                  projection = CdmOpenLayers.projections.epsg_4326;
793
                  // mapResponseObj.bbox bounds are always returned in EPSG:4326 since the point service does not know about the projection
794
                  // console.log("createDataLayer() POINT: referenceProjection()=" + referenceProjection() + ", map.getProjectionObject()=" + map.getProjectionObject() );
795
                  processDataBounds(projection, newBounds, dataType, layerDataLoaded);
796
                } else {
797
                    // IMPORTANT!!!!!!!
798
                    // with the latest upgrade of Geoserver the bbox value as returned by area.php has changed.
799
                    // Formerly areas.php did return the bbox in the projection of the layer now it seems to be
800
                    // always in EPSG:4326, which is a progress in some sense. Now the point.php and area.php
801
                    // behave consistently. (2020-10-20)
802
                    //
803
                    // the below code is kept anyway just in case something changes again.
804
                    // --------------------------------------
805
                    // dataType == AREA
806
                    // the bounds are in the projection of the data layer
807
                    // here we expect that all data layers are in the same projection and will use the first data layer as reference
808
                    // the edit map service is most probably working the same and is not expected to be able to handle multiple data layers
809
                    // with different projections
810
                    readProjection(baseLayers[0], function(projection) {
811
                    processDataBounds(projection, newBounds, dataType, layerDataLoaded);
812
                  })
813
                }
814

    
815
                console.log("createDataLayer() " + dataType + ": transforming newBounds " + newBounds + " from projection=" +  projection + " to referenceProjection()=" + referenceProjection());
816
                newBounds.transform(projection, referenceProjection());
817
                if(dataBounds !== null){
818
                  dataBounds.extend(newBounds);
819
                } else if(newBounds !== undefined){
820
                  dataBounds = newBounds;
821
                }
822

    
823
                zoomToBounds = dataBounds;
824
                console.log("createDataLayer() : viewport zoomToBounds are now: " + zoomToBounds);
825
                zoomToClosestLevel = false;
826
              }
827
            }
828

    
829
            if(legendImgSrc != null && opts.legendPosition !== undefined && mapResponseObj.legend !== undefined){
830
                var legendSrcUrl = mapResponseObj.geoserver + legendImgSrc + mapResponseObj.legend;
831
                addLegendAsElement(legendSrcUrl);
832
                //addLegendAsLayer(legendSrcUrl, map);
833
            }
834

    
835
            return layers;
836
          }
837

    
838
        };
839

    
840
      /**
841
       * transforms the newBounds from the projection to the referenceProjection() and finally calculates the
842
       * zoomBounds for the viewport.
843
       *
844
       * @param projection
845
       * @param newBounds
846
       * @param layerDataTypeString
847
       *    Only used for logging, (either "AREA" or "POINT")
848
       * @param callback
849
       */
850
        var processDataBounds = function(projection, newBounds, layerDataTypeString, callback){
851

    
852
          console.log("createDataLayer() " + layerDataTypeString + ": transforming newBounds " + newBounds + " from projection=" +  projection + " to referenceProjection()=" + referenceProjection());
853
          newBounds.transform(projection, referenceProjection());
854
          if(dataBounds !== null){
855
            dataBounds.extend(newBounds);
856
          } else if(newBounds !== undefined){
857
            dataBounds = newBounds;
858
          }
859

    
860
          zoomToBounds = dataBounds;
861
          console.log("createDataLayer() : viewport zoomToBounds are now: " + zoomToBounds);
862
          zoomToClosestLevel = false;
863
          callback();
864
        };
865

    
866
      /**
867
       * Get the crs data from the WFS and read the projection name from it. Finally the supplied callback will
868
       * be called with the matching projection object as parameter.
869
       * @param layer
870
       * @param callback
871
       *   Function(Projection projection)
872
       */
873
        var readProjection = function(layer, callback){
874

    
875
          var projection = layer.projection;
876

    
877
          if(!projection) {
878
            projection = layerProjections[layer.name];
879
          }
880

    
881
          if(projection) {
882
            callback(projection);
883
          } else {
884
            // asking the edit map server would be the best:
885
            //    > http://edit.africamuseum.be/geoserver/topp/ows?service=WFS&version=1.0.0&request=GetFeature&typeName=topp:euromed_2013&maxFeatures=1&outputFormat=application/json
886
            // or
887
            //    > http://edit.africamuseum.be/geoserver/topp/ows?service=WFS&request=getCapabilities'
888
            // but the latter returns only XML
889
            var parameters = {
890
              service: 'WFS',
891
              version: '1.0.0',
892
              request: 'GetFeature',
893
              typeName: layer.name,
894
              maxFeatures: 1, // only one feature
895
              outputFormat: 'text/javascript',
896
              format_options: 'callback:getJson'
897
            };
898

    
899
            jQuery.ajax({
900
              url: opts.wfsRootUrl + "?" + jQuery.param(parameters),
901
              dataType: 'json',
902
              success: function(data, textStatus, jqXHR){
903
                if(data.crs && data.crs.type && data.crs.properties.code){
904
                  var projectionName = data.crs.type + "_" + data.crs.properties.code;
905
                  log("projection name found in WFS response:" + projectionName);
906
                  projection = CdmOpenLayers.projections[projectionName.toLowerCase()];
907
                  callback(projection);
908
                }
909
              },
910
              error : function(jqXHR, textStatus, errorThrown) {
911
                log("projection name not found in WFS response, due to error: " + textStatus);
912
                projection = CdmOpenLayers.projections.epsg_4326;
913
                callback(projection);
914
              }
915

    
916
            });
917
          }
918
        };
919

    
920
        /**
921
         *
922
         */
923
        var addLegendAsElement= function(legendSrcUrl){
924

    
925
            var legendElement = jQuery('<div class="openlayers_legend"></div>');
926
            var legendImage = jQuery('<img src="' + legendSrcUrl + '"/>');
927
            legendElement
928
                .css('opacity', opts.legendOpacity)
929
                .css('position', 'relative')
930
                .css('z-index', '1002')
931
                .css('top', -mapElement.height());
932
            legendImage.load(function () {
933
                jQuery(this).parent()
934
                    .css('left', getWidth() - jQuery(this).width())
935
                    .width(jQuery(this).width());
936
                // reset height to original value
937
                adjustHeight();
938
            });
939
            legendElement.html(legendImage);
940
            mapElement.after(legendElement);
941
        };
942

    
943
         var adjustLegendAsElementPosition = function (){
944
           var legendContainer = mapContainerElement.children('.openlayers_legend');
945
           var legendImage = legendContainer.children('img');
946
           legendContainer.css('top', -mapElement.height())
947
             .css('left', getWidth() - legendImage.width());
948
         };
949

    
950

    
951
        var addLegendAsLayer= function(legendSrcUrl, map){
952
            var w, h;
953

    
954
            // 1. download image to find height and width
955
            mapElement.after('<div class="openlayers_legend"><img src="' + legendSrcUrl + '"></div>');
956
            mapElement.next('.openlayers_legend').css('display', 'none').css('opacity', opts.legendOpacity).find('img').load(function () {
957

    
958
                w = mapElement.next('.openlayers_legend').find('img').width();
959
                h = mapElement.next('.openlayers_legend').find('img').height();
960
                mapElement.next('.openlayers_legend').remove();
961

    
962
//              createLegendLayer();
963
//              // 2. create the Legend Layer
964
                //TODO createLegendLayer as inner function seems like an error
965
//              var createLegendLayer = function(){
966
                //
967
                //
968
//              var legendLayerOptions={
969
//              maxResolution: '.$maxRes.',
970
//              maxExtent: new OpenLayers.Bounds(0, 0, w, h)
971
//              };
972
                //
973
//              var legendLayer = new OpenLayers.Layer.Image(
974
//              'Legend',
975
//              legendSrcUrl,
976
//              new OpenLayers.Bounds(0, 0, w, h),
977
//              new OpenLayers.Size(w, h),
978
//              imageLayerOptions);
979
//              };
980
            });
981

    
982

    
983
        };
984

    
985
        /**
986
         * merge 2 Url query strings
987
         */
988
        var mergeQueryStrings = function(queryStr1, queryStr2){
989
            if(queryStr1.charAt(queryStr1.length - 1) != '&'){
990
                queryStr1 += '&';
991
            }
992
            if(queryStr2.charAt(0) == '&'){
993
                return queryStr1 + queryStr2.substr(1);
994
            } else {
995
                return queryStr1 + queryStr2;
996
            }
997

    
998
        };
999

    
1000
        /**
1001
         *
1002
         */
1003
        var createBaseLayers = function( baseLayerNames, defaultBaseLayerName, customWMSBaseLayerData){
1004

    
1005
            for(var i = 0; i <  baseLayerNames.length; i++) {
1006
                // create the layer
1007
                if (baseLayerNames[i] === "custom_wms_base_layer_1"){
1008
                    wmsBaseLayer =createWMSLayer(
1009
                            customWMSBaseLayerData.name,
1010
                            customWMSBaseLayerData.url,
1011
                            customWMSBaseLayerData.params,
1012
                            customWMSBaseLayerData.projection,
1013
                            customWMSBaseLayerData.proj4js_def,
1014
                            customWMSBaseLayerData.units,
1015
                            customWMSBaseLayerData.max_extent,
1016
                            customWMSBaseLayerData.untiled
1017
                    );
1018
                  wmsBaseLayer.setIsBaseLayer(true);
1019
                  baseLayers[i] = wmsBaseLayer;
1020
                } else {
1021
                    baseLayers[i] = window.CdmOpenLayers.getLayerByName(baseLayerNames[i]);
1022
                }
1023
                // set default baselayer
1024
                if(baseLayerNames[i] === defaultBaseLayerName){
1025
                    defaultBaseLayer = baseLayers[i];
1026
                }
1027

    
1028
            }
1029
        };
1030

    
1031
        /**
1032
         * returns the intersection of the bounds b1 and b2.
1033
         * The b1 and b2 do not intersect b1 will be returned.
1034
         *
1035
         * @param OpenLayers.Bounds b1
1036
         * @param OpenLayers.Bounds b2
1037
         *
1038
         * @return the bounds of the intersection between both rectangles
1039
         */
1040
        var intersectionOfBounds = function (b1, b2){
1041

    
1042
            if(b1.intersectsBounds(b2)){
1043

    
1044
                var left = Math.max(b1.left, b2.left);
1045
                var bottom = Math.max(b1.bottom, b2.bottom);
1046
                var right = Math.min(b1.right, b2.right);
1047
                var top = Math.min(b1.top, b2.top);
1048

    
1049
                return new OpenLayers.Bounds(left, bottom, right, top);
1050

    
1051
            } else {
1052
                return b1;
1053
            }
1054
        };
1055

    
1056
        /**
1057
         *
1058
         * @param b OpenLayers.Bounds to crop
1059
         * @param aspectRatio as fraction of width/height as float value
1060
         *
1061
         * @return the bounds cropped to the given aspectRatio
1062
         */
1063
        var cropBoundsToAspectRatio = function (b, aspectRatio){
1064

    
1065
            var cropedB = b.clone();
1066

    
1067
            if(aspectRatio === 1){
1068
                return cropedB;
1069
            }
1070

    
1071
            /*
1072
             * LonLat:
1073
             *   lon {Float} The x-axis coordinate in map units
1074
             *   lat {Float} The y-axis coordinate in map units
1075
             */
1076
            var center = cropedB.getCenterLonLat();
1077
            var dist;
1078
            if(aspectRatio < 1){
1079
                dist = (b.getHeight() / 2) * aspectRatio;
1080
                cropedB.top = center.lat + dist;
1081
                cropedB.cropedBottom = center.lat - dist;
1082
            } else if(aspectRatio > 1){
1083
                dist = (b.getWidth() / 2) / aspectRatio;
1084
                cropedB.left = center.lon - dist;
1085
                cropedB.right = center.lon + dist;
1086
            }
1087
            return cropedB;
1088
        };
1089

    
1090
        /**
1091
         * returns the version number contained in the version string:
1092
         *   v1.1 --> 1.1
1093
         *   v1.2_dev --> 1.2
1094
         */
1095
        var mapServerVersionNumber = function() {
1096
            var pattern = /v([\d\.]+).*$/;
1097
            var result;
1098
            if (result = mapserverVersion.match(pattern) !== null) {
1099
                return result[0];
1100
            } else {
1101
                return null;
1102
            }
1103
        };
1104

    
1105

    
1106

    
1107
      /**
1108
       * returns the zoom to bounds.
1109
       *
1110
       * @param bboxString
1111
       *     a string representation of the bounds in degree for epsg_4326
1112
       *
1113
       * @return the bboxstring projected onto the layer and intersected with the maximum extent of the layer
1114
       */
1115
      var calculateZoomToBounds = function(bboxString){
1116
        var zoomToBounds;
1117
        if(bboxString) {
1118
          zoomToBounds = OpenLayers.Bounds.fromString(bboxString);
1119
          if(referenceProjection().proj.projName){
1120
            // SpericalMercator is not supporting the full extent -180,-90,180,90
1121
            // crop if need to -179, -85, 179, 85
1122
            if(zoomToBounds.left < -179){
1123
              zoomToBounds.left =  -179;
1124
            }
1125
            if(zoomToBounds.bottom < -85){
1126
              zoomToBounds.bottom =  -85;
1127
            }
1128
            if(zoomToBounds.right > 179){
1129
              zoomToBounds.right =  179;
1130
            }
1131
            if(zoomToBounds.top > 85){
1132
              zoomToBounds.top = 85;
1133
            }
1134
          }
1135
          // transform bounding box given in degree values to the projection of the base layer
1136
          zoomToBounds.transform(CdmOpenLayers.projections.epsg_4326, referenceProjection());
1137
        } else if(referenceMaxExtent()) {
1138
          zoomToBounds = referenceMaxExtent();
1139
          // no need to transform since the bounds are obtained from the layer
1140
        } else {
1141
          // use the more narrow bbox of the SphericalMercator to avoid reprojection problems
1142
          // SpericalMercator is not supporting the full extent!
1143
          zoomToBounds = CdmOpenLayers.mapExtends.epsg_900913;
1144
          // transform bounding box given in degree values to the projection of the base layer
1145
          zoomToBounds.transform(CdmOpenLayers.projections.epsg_4326, referenceProjection());
1146
        }
1147

    
1148
        zoomToBounds = intersectionOfBounds(referenceMaxExtent(), zoomToBounds);
1149

    
1150
        log("zoomBounds calculated: " + zoomToBounds.toString());
1151

    
1152
        return zoomToBounds;
1153
      };
1154

    
1155
      var log = function(message, addTimeStamp){
1156
        var timestamp = '';
1157
        if(addTimeStamp === true){
1158
          var time = new Date();
1159
          timestamp = '[' + time.getSeconds() + '.' + time.getMilliseconds() + ' s] ';
1160
        }
1161
        console.log(timestamp + message);
1162
      };
1163

    
1164
      var makeWMSLayerOptions = function(projection, proj4js_def, maxExtent, units, untiled) {
1165
        var wmsOptions = {
1166
            isBaseLayer: false,
1167
            displayInLayerSwitcher: true,
1168
            tileOptions: {
1169
                eventListeners: {
1170
                    'loaderror': function (evt) {
1171
                        reportWMSError(evt.object.layer);
1172
                    }
1173
                }
1174
            }
1175
        };
1176

    
1177
        if (projection) {
1178
          if (proj4js_def) {
1179
            // in case projection has been defined for the layer and if there is also
1180
            // a Proj4js.defs, add it!
1181
            Proj4js.defs[projection] = proj4js_def;
1182
          }
1183
          wmsOptions.projection = projection;
1184
          if (maxExtent === null) {
1185
            maxExtent = CdmOpenLayers.mapExtends.epsg_4326.clone();
1186
            maxExtent.transform(CdmOpenLayers.projections.epsg_4326, projection);
1187
          }
1188
        } else {
1189
          // use the projection and maxextent of the base layer
1190
          maxExtent = referenceMaxExtent();
1191
        }
1192

    
1193
        if (maxExtent) {
1194
          wmsOptions.maxExtent = maxExtent;
1195
        }
1196

    
1197
        if (units) {
1198
          wmsOptions.units = units;
1199
        }
1200

    
1201
        if (true || untiled) {
1202
          wmsOptions.singleTile = true;
1203
          wmsOptions.ratio = opts.aspectRatio;
1204
        }
1205

    
1206
        return wmsOptions;
1207
      };
1208

    
1209
      var applyLayerZoomBounds = function(event){
1210
            var layer = event.object;
1211
            zoomToBounds = layer.getDataExtent();
1212
            log("data bounds of layer as zoom bounds: " + zoomToBounds.toString());
1213
            layerDataLoaded();
1214
      };
1215

    
1216
      var disablePolygonFeatureClick = function(event){
1217
          var layer = event.object;
1218
          var kmlLayerElement = jQuery('#' + layer.id);
1219
          //log("KML Layer DOM element: " + kmlLayerElement);
1220
          kmlLayerElement.find('path').css('pointer-events', 'none');
1221
      }
1222

    
1223
      /**
1224
       * Creates a WMS Base layer
1225
       * @param String name
1226
       *     A name for the layer
1227
       * @param String url
1228
       *     Base url for the WMS (e.g.  http://wms.jpl.nasa.gov/wms.cgi)
1229
       * @param Object params
1230
       *     An object with key/value pairs representing the GetMap query string parameters and parameter values.
1231
       * @param Object projection
1232
       *    A OpenLayers.Projection object
1233
       */
1234
      var createWMSLayer= function(name, url, params, projection, proj4js_def, units, maxExtent, untiled){
1235

    
1236
        console.log("creating WMS Layer " + name);
1237

    
1238
        var wmsOptions = makeWMSLayerOptions(projection, proj4js_def, maxExtent, units, untiled);
1239

    
1240
        var wmsLayer = new OpenLayers.Layer.WMS(
1241
            name,
1242
            url,
1243
            params,
1244
            wmsOptions
1245
          );
1246

    
1247
          if(wmsLayer === null){
1248
            console.log("Error creating WMS Layer");
1249
          }
1250

    
1251
          return  wmsLayer;
1252
        };
1253

    
1254
      var onKmlFeatureSelect = function(event) {
1255
        var feature = event.feature;
1256
        // Since KML is user-generated, do naive protection against
1257
        // Javascript.
1258
        var content = "";
1259
        if(feature.attributes.name){
1260
            content += "<h3>" + feature.attributes.name + "</h3>";
1261
        }
1262
        if(feature.attributes.description) {
1263
            // ${specimen-base-url}
1264
            var description = feature.attributes.description;
1265
            description = description.replace('${occurrence-link-base-url}', opts.specimenPageBaseUrl);
1266
            //description = description.replace('${specimen-link-text}', opts.specimenLinkText); // no longer used
1267
            content += "<p>" + description + "</p>";
1268
        }
1269
        if (content.search("<script") != -1) {
1270
            content = "Content contained Javascript! Escaped content below.<br>" + content.replace(/</g, "&lt;");
1271
        }
1272
        if(content.length > 0){
1273
            popup = new OpenLayers.Popup.FramedCloud("balloon",
1274
                feature.geometry.getBounds().getCenterLonLat(),
1275
                new OpenLayers.Size(250, 150),
1276
                content,
1277
                null, true, onKmlPopupClose);
1278
                popup.autoSize = false;
1279
                // popup.imageSize = new OpenLayers.Size(1276, 736);
1280
                // popup.fixedRelativePosition = true;
1281
                // popup.maxSize = new OpenLayers.Size(50, 50);
1282
            feature.popup = popup;
1283
            map.addPopup(popup);
1284
        }
1285
      };
1286
      var onKmlFeatureUnselect =   function(event) {
1287
            var feature = event.feature;
1288
            if(feature.popup) {
1289
                map.removePopup(feature.popup);
1290
                feature.popup.destroy();
1291
                delete feature.popup;
1292
            }
1293
        };
1294
      var onKmlPopupClose = function(evt) {
1295
          kmlSelectControl.unselectAll();
1296
        };
1297

    
1298
    var layerLoadingControl = function() {
1299

    
1300
      var control = new OpenLayers.Control();
1301

    
1302
      OpenLayers.Util.extend(control, {
1303

    
1304
        LAYERS_LOADING: 0,
1305

    
1306
        type: 'LayerLoading',
1307
        title: 'Layer loading',
1308

    
1309
        updateState: function () {
1310
          if(this.div != null){
1311
            if (this.LAYERS_LOADING > 0) {
1312
              this.div.style.display = "block";
1313
            } else {
1314
              this.div.style.display = "none";
1315
            }
1316
          }
1317
        },
1318

    
1319
        updateSize: function () {
1320
          this.div.style.width = this.map.size.w + "px";
1321
          this.div.style.height = this.map.size.h  + "px";
1322
          this.div.style.textAlign = "center";
1323
          this.div.style.lineHeight = this.map.size.h  + "px";
1324
          this.div.style.backgroundColor= 'rgba(255, 255, 255, 0.3)';
1325
        },
1326

    
1327
        counterIncrease: function (layer) {
1328
          this.control.LAYERS_LOADING++;
1329
          log(' > loading start : ' + this.layer.name + ' ' + this.control.LAYERS_LOADING, true);
1330
          this.control.updateState();
1331
        },
1332

    
1333
        counterDecrease: function (layer) {
1334
          this.control.LAYERS_LOADING--;
1335
          log(' > loading end : ' + this.layer.name + ' ' + this.control.LAYERS_LOADING, true);
1336
          this.control.updateState();
1337
        },
1338

    
1339
        draw: function () {
1340

    
1341
          // call the default draw function to initialize this.div
1342
          OpenLayers.Control.prototype.draw.apply(this, arguments);
1343

    
1344
          this.map.events.register('updatesize', this, function(e){
1345
              this.updateSize();
1346
            }
1347
          );
1348

    
1349
          var loadingIcon = document.createElement("i");
1350
          var fa_class = document.createAttribute("class");
1351
          fa_class.value = "fa fa-refresh fa-spin fa-sync-alt fa-5x";
1352
          loadingIcon.attributes.setNamedItem(fa_class);
1353

    
1354
          this.updateSize();
1355

    
1356
          this.div.appendChild(loadingIcon);
1357

    
1358
          this.registerEvents();
1359

    
1360
          return this.div;
1361
        },
1362

    
1363
        registerEvents: function() {
1364

    
1365
          this.map.events.register('preaddlayer', this, function(e){
1366
            console.log(" > preaddlayer " + e.layer.name);
1367
            e.layer.events.register('loadstart', {control: this, layer: e.layer}, this.counterIncrease);
1368
            e.layer.events.register('loadend', {control: this, layer: e.layer}, this.counterDecrease);
1369
          });
1370
        }
1371

    
1372
      });
1373

    
1374
      return control;
1375
    }
1376

    
1377
    var errorMessageControl = function() {
1378

    
1379
            var control = new OpenLayers.Control();
1380

    
1381
            OpenLayers.Util.extend(control, {
1382

    
1383
                messageText: "The map is currently broken due to problems with the map server.",
1384
                id: 'OpenLayers_Control_ErrorMessages',
1385
                type: 'ErrorMessages',
1386
                title: 'Error messages',
1387

    
1388
                errorDetails: null,
1389

    
1390
                updateState: function () {
1391
                    if(this.div != null){
1392
                        if (this.LAYERS_LOADING > 0) {
1393
                            this.div.style.display = "block";
1394
                        } else {
1395
                            this.div.style.display = "none";
1396
                        }
1397
                    }
1398
                },
1399

    
1400
                updateSize: function () {
1401
                    this.div.style.width = this.map.size.w + "px";
1402
                    this.div.style.height = this.map.size.h  + "px";
1403
                },
1404

    
1405
                addErrorMessage: function(errorText){
1406
                    var li1 = document.createElement("li");
1407
                    li1.appendChild(document.createTextNode(errorText));
1408
                    this.errorDetails.appendChild(li1);
1409
                },
1410
                hide: function(){
1411
                    this.div.style.display = 'none';
1412
                },
1413
                show: function(){
1414
                    this.div.style.display = 'flex';
1415
                    this.div.style.position = 'relative';
1416
                    this.div.style.padding = '10px 20px 20px 70px';
1417
                },
1418

    
1419
                draw: function () {
1420

    
1421
                    // call the default draw function to initialize this.div
1422
                    OpenLayers.Control.prototype.draw.apply(this, arguments);
1423

    
1424
                    this.map.events.register('updatesize', this, function(e){
1425
                            this.updateSize();
1426
                        }
1427
                    );
1428

    
1429
                    // using flexbox here!
1430
                    //see this.show();
1431
                    this.div.style.justifyContent = "center";
1432
                    this.div.style.alignItems = "center";
1433

    
1434
                    this.errorDetails = document.createElement("ul");
1435
                    this.errorDetails.setAttribute('style', 'font-size:80%');
1436

    
1437
                    var contentDiv = document.createElement("div");
1438
                    contentDiv.appendChild(document.createTextNode(this.messageText));
1439

    
1440
                    contentDiv.appendChild(this.errorDetails);
1441
                    this.div.setAttribute('style', 'background-color: rgba(200, 200, 200, 0.3);');
1442
                    this.div.appendChild(contentDiv);
1443

    
1444
                    this.updateSize();
1445
                    this.hide();
1446
                    return this.div;
1447
                },
1448
            });
1449
            return control;
1450
        }
1451

    
1452
    }; // end of CdmOpenLayers.Map
1453
})();
1454

    
1455

    
1456

    
1457

    
1458

    
1459

    
1460

    
1461

    
1462

    
(2-2/2)