Project

General

Profile

Download (43.9 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
    debug: true,
41
    /**
42
     * allows the map to display parts of the layers which are outside
43
     * the maxExtent if the aspect ratio of the map and of the baselayer
44
     * are not equal
45
     */
46
    displayOutsideMaxExtent: false,
47
    //  customWMSBaseLayerData: {
48
    //  name: "Euro+Med",
49
    //  url: "http://edit.africamuseum.be/geoserver/topp/wms",
50
    //  params: {layers: "topp:em_tiny_jan2003", format:"image/png", tiled: true},
51
    //  projection: "EPSG:7777777",
52
    //  maxExtent: "-1600072.75, -1800000, 5600000, 5850093",
53
    //  units: 'm'
54
    //  }
55
    customWMSBaseLayerData: {
56
        name: null,
57
        url: null,
58
        params: null,
59
        projection: null,
60
        proj4js_def: null,
61
        max_extent: null,
62
        units: null,
63
        untiled: null
64
    },
65
    wmsOverlayLayerData: {
66
      name: null,
67
      url: null,
68
      params: null,
69
      untiled: null
70
    },
71
    /**
72
     * when true the map is made resizable by adding the jQueryUI widget resizable
73
     * to the map container. This feature requires that the jQueryUI is loaded
74
     */
75
    resizable: false,
76
    wfsRootUrl: 'http://edit.africamuseum.be/geoserver/topp/ows'
77
  };
78
})(jQuery);
79

    
80
/**************************************************************************
81
 *                          CdmOpenLayers
82
 **************************************************************************/
83
(function() {
84

    
85
    /**
86
     * The CdmOpenLayers namespace definition
87
     */
88
    window.CdmOpenLayers  = (function () {
89

    
90
        // EPSG:3857 from http://spatialreference.org/ref/sr-org/6864/proj4/
91
        // OpenStreetMap etc
92
        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';
93
        Proj4js.defs["EPSG:7777777"] = '+proj=lcc + 42 + 56 + 35 + 24 + 3000000 + 100000';
94

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

    
112
        // make public by returning an object
113
        return {
114
            projections: projections,
115
            mapExtends: mapExtends,
116
            getMap: function (){},
117
            getLayerByName: function(layerName){} // initially empty function, will be populated by openlayers_layers.js
118
        };
119

    
120
    })(); // end of namespace definition for CdmOpenLayers
121

    
122
    /**
123
     * The CdmOpenLayers.Map constructor
124
     * @param mapElement
125
     * @param mapserverBaseUrl
126
     * @param mapserverVersion
127
     * @param opts
128
     * @returns
129
     */
130
    window.CdmOpenLayers.Map = function(mapElement, mapserverBaseUrl, mapserverVersion, opts){
131

    
132
      var mapServicePath = '/edit_wp5';
133

    
134
      // firebug console stub (avoids errors if firebug is not active)
135
      if(typeof console === "undefined") {
136
          console = { log: function() { } };
137
      }
138

    
139
      // sanitize given options
140
      try {
141
          opts.customWMSBaseLayerData.max_extent = OpenLayers.Bounds.fromString(opts.customWMSBaseLayerData.max_extent);
142
      } catch(e){
143
          opts.customWMSBaseLayerData.max_extent = null;
144
      }
145

    
146

    
147
      var legendImgSrc = null;
148

    
149
      var map = null;
150

    
151
      var infoElement = null;
152

    
153
      var baseLayers = [];
154

    
155
      var defaultBaseLayer = null;
156

    
157
      /**
158
       * The top most layer which will be places above all data layers
159
       *
160
       * @type {null}
161
       */
162
      var wmsOverlay = null;
163

    
164
      /**
165
       * Default bounding box for map viewport in the projection of the base layer.
166
       * as defined by the user, can be null.
167
       *
168
       * These are bounds in the epsg_4326 projection, and will be transformed to the baselayer projection.
169
       *
170
       * @type string
171
       */
172
      var defaultBaseLayerBoundingBox = "-180,-90,180,90";
173

    
174
      /**
175
       * bounding box for map viewport as defined by the user, can be null.
176
       *
177
       * These are bounds in the projection of the base layer.
178
       *
179
       * @type string
180
       */
181
      var boundingBox = null;
182

    
183
      /**
184
       * Bounds for the view port calculated from the data layer responses.
185
       * These are either calculated by the minimum bounding box which
186
       * encloses the data in the data layers, or it is equal to the
187
       * boundingBox as defined by the user.
188
       *
189
       * These are bounds in the projection of the base layer.
190
       *
191
       * @see boundingBox
192
       *
193
       * @type OpenLayers.Bounds
194
       */
195
      var dataBounds = null;
196

    
197
      /**
198
       * Final value for the view port, calculated from the other bounds.
199
       *
200
       * These are bounds in the projection of the base layer.
201
       *
202
       * @type OpenLayers.Bounds
203
       */
204
      var zoomToBounds = null;
205

    
206
      var zoomToClosestLevel = true;
207

    
208
      var LAYER_DATA_CNT = 0;
209

    
210
      /* this is usually the <div id="openlayers"> element */
211
      var mapContainerElement = mapElement.parent();
212

    
213
      var defaultControls = [
214
         new OpenLayers.Control.PanZoom(),
215
         new OpenLayers.Control.Navigation(
216
           {
217
             zoomWheelEnabled: false,
218
             handleRightClicks:true,
219
             zoomBoxKeyMask: OpenLayers.Handler.MOD_CTRL
220
           }
221
         )
222
      ];
223

    
224

    
225
      var layerByNameMap = {
226
              tdwg1: 'topp:tdwg_level_1',
227
              tdwg2: 'topp:tdwg_level_2',
228
              tdwg3: 'topp:tdwg_level_3',
229
              tdwg4: 'topp:tdwg_level_4'
230
      };
231

    
232
      /**
233
       * Known projections by layer name. This map helps avoid requesting the server for the
234
       * projection. See readProjection()
235
       */
236
      var layerProjections = {
237
        'topp:tdwg_level_1': CdmOpenLayers.projections.epsg_4326,
238
        'topp:tdwg_level_2': CdmOpenLayers.projections.epsg_4326,
239
        'topp:tdwg_level_3': CdmOpenLayers.projections.epsg_4326,
240
        'topp:tdwg_level_4': CdmOpenLayers.projections.epsg_4326,
241
        'topp:phytogeographical_regions_of_greece': CdmOpenLayers.projections.epsg_4326,
242
        'topp:euromed_2013': CdmOpenLayers.projections.epsg_7777777,
243
        'topp:flora_cuba_2016': CdmOpenLayers.projections.epsg_4326
244
      };
245

    
246
      if(opts.resizable === true) {
247
        // resizable requires jQueryUI to  be loaded!!!
248
        mapContainerElement.resizable({
249
          resize: function( event, ui ) {
250
            map.updateSize();
251
            //   this.printInfo();
252
          }
253
        });
254
      }
255

    
256
        /**
257
         *
258
         */
259
        this.create = function(){ // public function
260

    
261
          // set the height of the container element
262
          adjustHeight();
263

    
264
          // register for resize events to be able to adjust the map aspect ratio and legend position
265
          jQuery( window ).resize(function() {
266
            adjustHeight();
267
            adjustLegendAsElementPosition();
268
          });
269

    
270
          createBaseLayers(opts.baseLayerNames, opts.defaultBaseLayerName, opts.customWMSBaseLayerData);
271

    
272
          initMap();
273

    
274

    
275
          if(opts.boundingBox){
276
            boundingBox = OpenLayers.Bounds.fromString(opts.boundingBox);
277
          }
278

    
279
          // -- Distribution Layer --
280
          var mapServiceRequest;
281
          var distributionQuery = mapElement.attr('distributionQuery');
282

    
283
          if(distributionQuery !== undefined){
284
            distributionQuery = mergeQueryStrings(distributionQuery, '&recalculate=false');
285
            if(typeof legendPosition === 'number'){
286
              distributionQuery = mergeQueryStrings(distributionQuery, 'legend=1&mlp=' + opts.legendPosition);
287
            }
288
            if(opts.boundingBox){
289
              distributionQuery = mergeQueryStrings(distributionQuery, 'bbox=' + boundingBox);
290
            }
291

    
292
            distributionQuery = mergeQueryStrings(distributionQuery, 'callback=?');
293
            var legendFormatQuery = mapElement.attr('legendFormatQuery');
294
            if(legendFormatQuery !== undefined){
295
              legendImgSrc = mergeQueryStrings('/GetLegendGraphic?SERVICE=WMS&VERSION=1.1.1', legendFormatQuery);
296
            }
297

    
298
            mapServiceRequest = mapserverBaseUrl + mapServicePath + '/' + mapserverVersion + '/rest_gen.php?' + distributionQuery;
299

    
300
            LAYER_DATA_CNT++;
301
            jQuery.ajax({
302
              url: mapServiceRequest,
303
              dataType: "jsonp",
304
              success: function(data){
305
                  var layers = createDataLayer(data, "AREA");
306
                  addLayers(layers);
307
                // layerDataLoaded(); will be called after reading the projection from the WFS for the data layer
308
              }
309
            });
310
          }
311

    
312
          // -- Occurrence Layer --
313
          var occurrenceQuery = mapElement.attr('occurrenceQuery');
314
          if(occurrenceQuery !== undefined){
315
            occurrenceQuery = mergeQueryStrings(occurrenceQuery, '&recalculate=false');
316
//              if(typeof legendPosition == 'number'){
317
//              occurrenceQuery = mergeQueryStrings(distributionQuery, 'legend=1&mlp=' + opts.legendPosition);
318
//              }
319

    
320

    
321
            occurrenceQuery = mergeQueryStrings(occurrenceQuery, 'callback=?');
322
//              var legendFormatQuery = mapElement.attr('legendFormatQuery');
323
//              if(legendFormatQuery !== undefined){
324
//              legendImgSrc = mergeQueryStrings('/GetLegendGraphic?SERVICE=WMS&VERSION=1.1.1', legendFormatQuery);
325
//              }
326
            if(opts.boundingBox){
327
              occurrenceQuery = mergeQueryStrings(occurrenceQuery, 'bbox=' + boundingBox);
328
            }
329

    
330
            mapServiceRequest = mapserverBaseUrl + mapServicePath + '/' + mapserverVersion + '/rest_gen.php?' + occurrenceQuery;
331

    
332
            LAYER_DATA_CNT++;
333
            jQuery.ajax({
334
              url: mapServiceRequest,
335
              dataType: "jsonp",
336
              success: function(data){
337
                  var layers = createDataLayer(data, "POINT");
338
                  addLayers(layers);
339
                  // layerDataLoaded(); will be called after reading the projection from the WFS for the data layer
340
              }
341
            });
342
          }
343

    
344
          if(LAYER_DATA_CNT === 0) {
345
            // a map only with base layer
346
            initPostDataLoaded();
347
          }
348

    
349
          // -- Overlay Layer --
350
          if(opts.wmsOverlayLayerData.params){
351
            overlay_layer_params = opts.wmsOverlayLayerData.params;
352
            overlay_layer_params.transparent=true;
353
            wmsOverlay = createWMSLayer(
354
              opts.wmsOverlayLayerData.name,
355
              opts.wmsOverlayLayerData.url,
356
              overlay_layer_params,
357
              null,
358
              null,
359
              null,
360
              null,
361
              opts.wmsOverlayLayerData.untiled
362
            );
363

    
364
            if(map.addLayer(wmsOverlay)){
365
              wmsOverlay.setVisibility(true);
366
              map.setLayerIndex(wmsOverlay, 100);
367
              log("Overlay wms added");
368
            } else {
369
              log("ERROR adding overlay wms layer")
370
            }
371
          }
372

    
373
          log("Map viewer creation complete.");
374

    
375
        };
376

    
377
      /**
378
       * Provides the layer name which can be used in WMS/WFS requests.
379
       * The layerData.tdwg field contains invalid layer names in case of
380
       * the tdwg layers. This function handles with this bug.
381
       *
382
       * @param layerData
383
       * @returns String
384
       *    the correct layer name
385
       */
386
        var fixLayerName = function(layerData){
387
         var wmsLayerName = layerByNameMap[layerData.tdwg];
388
         if(!wmsLayerName){
389
           wmsLayerName = "topp:" + layerData.tdwg;
390
         }
391
         return wmsLayerName;
392
        };
393

    
394
        var layerDataLoaded = function() {
395
          LAYER_DATA_CNT--;
396
          if(LAYER_DATA_CNT === 0){
397
            initPostDataLoaded();
398
          }
399
        };
400

    
401
        var initPostDataLoaded = function () {
402
          // all layers prepared, make them visible
403
          map.layers.forEach(function(layer){
404
            layer.setVisibility(true);
405
          });
406

    
407
          // zoom to the zoomToBounds
408
          log(" > starting zoomToExtend: " + zoomToBounds + ", zoomToClosestLevel: " + zoomToClosestLevel, true);
409
          map.zoomToExtent(zoomToBounds, zoomToClosestLevel);
410

    
411

    
412
          if(map.getZoom() > opts.maxZoom){
413
            map.zoomTo(opts.maxZoom);
414
          } else if(map.getZoom() < opts.minZoom){
415
            map.zoomTo(opts.minZoom);
416
          }
417

    
418
          // make sure the wmsOverlay is still on top
419
          if(wmsOverlay){
420
            map.setLayerIndex(wmsOverlay, 100);
421
          }
422

    
423
          log(" > zoomToExtend done", true);
424
        };
425

    
426
      /**
427
       * Returns  the projection of the defaultBaseLayer which is the
428
       * the projection to which all other layers and locations must be transformed.
429
       */
430
      var referenceProjection = function() {
431
        if(defaultBaseLayer){
432
          if(defaultBaseLayer.projection){
433
            return defaultBaseLayer.projection;
434
          } else if(defaultBaseLayer.sphericalMercator === true){
435
            return CdmOpenLayers.projections.epsg_900913;
436
          } else {
437
            log("Error - referenceProjection() defaultBaseLayer " + defaultBaseLayer.name + " misses projection information");
438
          }
439

    
440
        } else {
441
          log("Error - referenceProjection() defaultBaseLayer not set");
442
          return null;
443
        }
444
      };
445

    
446
      /**
447
       * Returns the maxExtent of the defaultBaseLayer.
448
       */
449
      var referenceMaxExtent = function() {
450
        if(defaultBaseLayer){
451
          return defaultBaseLayer.maxExtent;
452
        } else {
453
          log("Error - referenceMaxExtent() defaultBaseLayer not set");
454
          return null;
455
        }
456
      };
457

    
458
        var getHeight = function(){
459
          return mapContainerElement.width() / opts.aspectRatio;
460
        };
461

    
462
        var getWidth = function(){
463
          return mapContainerElement.width();
464
        };
465

    
466
        var adjustHeight = function() {
467
          mapContainerElement.css("height", getHeight());
468
        };
469

    
470
        /**
471
         * public function
472
         */
473
        this.registerEvents = function(events){
474
            for (var key in events) {
475
                if (events.hasOwnProperty(key)) {
476
                    map.events.register(key, map , events[key]);
477
                }
478
            }
479
        };
480

    
481

    
482
        /**
483
         * public function
484
         */
485
        this.getMap = function(){
486
            return map;
487
        };
488

    
489
        /**
490
         * Prints info on the current map into the jQuery element
491
         * as set in the options (opts.infoElement)
492
         * public function
493
         *
494
         */
495
        this.printInfo = function(){
496

    
497

    
498
            var mapExtendDegree = null;
499
            if(map.getExtent() != null){
500
              // If the baselayer is not yet set, getExtent() returns null.
501
              mapExtendDegree = map.getExtent().clone();
502
              mapExtendDegree.transform(map.baseLayer.projection, CdmOpenLayers.projections.epsg_4326);
503
            }
504

    
505
            var info = "<dl>";
506
            info += "<dt>zoom:<dt><dd>" + map.getZoom() + "</dd>";
507
            if(opts.debug){
508
                info += "<dt>map resolution:<dt><dd>" + map.getResolution() + "</dd>";
509
                info += "<dt>map max resolution:<dt><dd>" + map.getMaxResolution() + "</dd>";
510
                info += "<dt>map scale:<dt><dd>" + map.getScale() + "</dd>";
511
                info += "<dt>map width, height:<dt><dd>" + mapContainerElement.width() +  ", " + mapContainerElement.height() + "</dd>";
512
                info += "<dt>map aspect ratio:<dt><dd>" + mapContainerElement.width() / mapContainerElement.height() + "</dd>";
513
                if(map.getExtent() != null){
514
                  info += "<dt>map extent bbox:<dt><dd class=\"map-extent-bbox\">" + map.getExtent().toBBOX() + ", <strong>degree:</strong> <span class=\"degree-value\">" + mapExtendDegree.toBBOX() + "</span></dd>";
515
                  info += "<dt>map maxExtent bbox:<dt><dd>" + map.getMaxExtent().toBBOX() + "</dd>";
516
                  info += "<dt>baselayer extent bbox:<dt><dd class=\"baselayer-extent-bbox\">" +  map.baseLayer.getExtent().toBBOX() + ", <strong>degree:</strong> <span class=\"degree-value\">"
517
                    + map.baseLayer.getExtent().clone().transform(map.baseLayer.projection, CdmOpenLayers.projections.epsg_4326) + "</span></dd>"
518
                  info += "<dt>baselayer projection:<dt><dd>" + map.baseLayer.projection.getCode() + "</dd>";
519
                }
520
            } else {
521
                info += "<dt>bbox:<dt><dd>" + (mapExtendDegree !== null ? mapExtendDegree.toBBOX() : 'NULL') + "</dd>";
522
            }
523
            info += "</dl>";
524

    
525
            if(infoElement === null){
526
                infoElement = jQuery('<div class="map_info"></div>');
527
                mapElement.parent().after(infoElement);
528
            }
529
            infoElement.html(info);
530
        };
531

    
532
        /**
533
         * Initialize the Openlayers Map with the base layer
534
         */
535
        var initMap = function(){
536

    
537
          if(opts.showLayerSwitcher === true){
538
              defaultControls.push(new OpenLayers.Control.LayerSwitcher({'ascending':false}));
539
          }
540

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

    
543
//          var maxExtentByAspectRatio = cropBoundsToAspectRatio(defaultBaseLayer.maxExtent, getWidth/getHeight);
544
          var maxResolution = null;
545
          // gmaps has no maxExtent at this point, need to check for null
546
          if(referenceMaxExtent() !== null){
547
              maxResolution = Math[(opts.displayOutsideMaxExtent ? 'max' : 'min')](
548
                referenceMaxExtent().getWidth() / getWidth(),
549
                referenceMaxExtent().getHeight() / getHeight()
550
              );
551
          }
552
          console.log("mapOptions.maxResolution: " + maxResolution);
553
          console.log("mapOptions.restrictedExtent: " + referenceMaxExtent());
554

    
555
          map = new OpenLayers.Map(
556
            mapElement.attr('id'),
557
            {
558
              // defines the map ui elements and interaction features
559
              controls: defaultControls,
560

    
561
              // maxResolution determines the lowest zoom level and thus places the map
562
              // in its maximum extent into the available view port so that no additinal
563
              // gutter is visible and no parts of the map are hidden
564
              // see http://trac.osgeo.org/openlayers/wiki/SettingZoomLevels
565
              // IMPORTANT!!!
566
              // the maxResulution set here will be overwritten if the baselayers maxResolution
567
              // it is set
568
              maxResolution: maxResolution,
569

    
570
              // setting restrictedExtent the the maxExtent prevents from panning the
571
              // map out of its bounds
572
              restrictedExtent: referenceMaxExtent(),
573
//                      maxExtent: referenceMaxExtent(),
574

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

    
582
              eventListeners: opts.eventListeners,
583
              // creating the map with a null theme, since we include the stylesheet directly in the page
584
              theme: null
585

    
586
            }
587
          );
588

    
589
          //add the base layers
590

    
591
          addLayers(baseLayers);
592
          map.setBaseLayer(defaultBaseLayer);
593

    
594
          // calculate the bounds to zoom to
595
          zoomToBounds = calculateZoomToBounds(opts.boundingBox ? opts.boundingBox : defaultBaseLayerBoundingBox);
596
          // zoomToBounds = cropBoundsToAspectRatio(zoomToBounds, map.getSize().w / map.getSize().h);
597
          console.log("baselayer zoomToBounds: " + zoomToBounds);
598

    
599
        };
600

    
601
        var addLayers = function(layers){
602

    
603
          layers.forEach(function(layer){
604
            // layer.setVisibility(false);
605
          });
606

    
607
          map.addLayers(layers);
608
        };
609

    
610
        /**
611
         * add a distribution or occurrence layer
612
         *
613
         * @param mapResponseObj
614
         *   The reponse object returned by the edit map service
615
         * @param dataType
616
         *   either "AREA" or "POINT"
617
         */
618
        var createDataLayer = function(mapResponseObj, dataType){
619

    
620
          console.log("createDataLayer() : creating data layer of type " + dataType);
621

    
622
          dataLayerOptions = makeWMSLayerOptions();
623
          dataLayerOptions.displayOutsideMaxExtent = true; // move into makeWMSLayerOptions?
624

    
625
          var layers = [];
626
          // add additional layers, get them from the mapResponseObj
627
          if(mapResponseObj !== undefined){
628
            if(dataType === "POINT" && mapResponseObj.points_sld !== undefined){
629
              var pointLayer;
630
              // it is a response for an point map
631
              var geoserverUri;
632
              if(mapResponseObj.geoserver) {
633
                  geoserverUri = mapResponseObj.geoserver;
634
              } else {
635
                  // it is an old service which is not providing the corresponding geoserver URI, so we guess it
636
                  geoserverUri = mapserverBaseUrl + "/geoserver/wms";
637
              }
638

    
639
              //TODO points_sld should be renamed to sld in response + fill path to sld should be given
640
              pointLayer = new OpenLayers.Layer.WMS(
641
                'points',
642
                geoserverUri,
643
                {
644
                    layers: 'topp:rest_points',
645
                    transparent:"true",
646
                    format:"image/png"
647
                },
648
                dataLayerOptions
649
              );
650

    
651
              var sld = mapResponseObj.points_sld;
652
              if(sld.indexOf("http://") !== 0){
653
                  // it is an old servive which is not providing the full sdl URI, so we guess it
654
                  //  http://edit.africamuseum.be/synthesys/www/v1/sld/
655
                  //  http://edit.br.fgov.be/synthesys/www/v1/sld/
656
                  sld =  mapserverBaseUrl + "/synthesys/www/v1/sld/" + sld;
657
              }
658
              pointLayer.params.SLD = sld;
659

    
660
              layers.push(pointLayer);
661
            } else {
662
              // it is a response from for a distribution map
663
              console.log("createDataLayer() : start with adding distribution layers :");
664
              for ( var i in mapResponseObj.layers) {
665
                var layerData = mapResponseObj.layers[i];
666

    
667
                var layerName = fixLayerName(layerData);
668
                console.log(" " + i +" -> " +layerName);
669
                var layer = new OpenLayers.Layer.WMS(
670
                  layerName,
671
                  mapResponseObj.geoserver + "/wms",
672
                  {
673
                      layers: fixLayerName(layerData),
674
                      transparent:"true",
675
                      format:"image/png"
676
                  },
677
                  dataLayerOptions
678
                  );
679
                layer.params.SLD = layerData.sld;
680
                layer.setOpacity(opts.distributionOpacity);
681

    
682
                layers.push(layer);
683
              }
684
            }
685

    
686
            if(layers.length > 0) {
687
              // calculate zoomBounds using the first layer
688
              if(mapResponseObj.bbox !== undefined){
689
                var newBounds =  OpenLayers.Bounds.fromString( mapResponseObj.bbox );
690
                var projection;
691
                if(dataType === "POINT"){
692
                  projection = CdmOpenLayers.projections.epsg_4326;
693
                  // mapResponseObj.bbox are bounds  are always returned in EPSG:4326 since the point service does not know about the projection
694
                  // console.log("createDataLayer() POINT: referenceProjection()=" + referenceProjection() + ", map.getProjectionObject()=" + map.getProjectionObject() );
695
                  processDataBounds(projection, newBounds, dataType, layerDataLoaded);
696
                } else {
697
                  // Type == AREA
698
                  // the bounds are in the projection of the data layer
699
                  // here we expect that all data layers are in the same projection and will use the first data layer as reference
700
                  // the edit map service is most probably working the same and is not expected to be able to handle multiple data layers
701
                  // with different projections
702
                  readProjection(layers[0], function(projection) {
703
                    processDataBounds(projection, newBounds, dataType, layerDataLoaded);
704
                  })
705
                }
706

    
707
                console.log("createDataLayer() " + dataType + ": transforming newBounds " + newBounds + " from projection=" +  projection + " to referenceProjection()=" + referenceProjection());
708
                newBounds.transform(projection, referenceProjection());
709
                if(dataBounds !== null){
710
                  dataBounds.extend(newBounds);
711
                } else if(newBounds !== undefined){
712
                  dataBounds = newBounds;
713
                }
714

    
715
                zoomToBounds = dataBounds;
716
                console.log("createDataLayer() : viewport zoomToBounds are now: " + zoomToBounds);
717
                zoomToClosestLevel = false;
718
              }
719
            }
720

    
721
            if(legendImgSrc != null && opts.legendPosition !== undefined && mapResponseObj.legend !== undefined){
722
                var legendSrcUrl = mapResponseObj.geoserver + legendImgSrc + mapResponseObj.legend;
723
                addLegendAsElement(legendSrcUrl);
724
                //addLegendAsLayer(legendSrcUrl, map);
725
            }
726

    
727
            return layers;
728
          }
729

    
730
        };
731

    
732
      /**
733
       * transforms the newBounds from the projection to the referenceProjection() and finally calculates the
734
       * zoomBounds for the viewport.
735
       *
736
       * @param projection
737
       * @param newBounds
738
       * @param layerDataTypeString
739
       *    Only used for logging, (either "AREA" or "POINT")
740
       * @param callback
741
       */
742
        var processDataBounds = function(projection, newBounds, layerDataTypeString, callback){
743

    
744
          console.log("createDataLayer() " + layerDataTypeString + ": transforming newBounds " + newBounds + " from projection=" +  projection + " to referenceProjection()=" + referenceProjection());
745
          newBounds.transform(projection, referenceProjection());
746
          if(dataBounds !== null){
747
            dataBounds.extend(newBounds);
748
          } else if(newBounds !== undefined){
749
            dataBounds = newBounds;
750
          }
751

    
752
          zoomToBounds = dataBounds;
753
          console.log("createDataLayer() : viewport zoomToBounds are now: " + zoomToBounds);
754
          zoomToClosestLevel = false;
755
          callback();
756
        };
757

    
758
      /**
759
       * Get the crs data from the WFS and read the projection name from it. Finally the supplied callback will
760
       * be called with the matching projection object as parameter.
761
       * @param layer
762
       * @param callback
763
       *   Function(Projection projection)
764
       */
765
        var readProjection = function(layer, callback){
766

    
767
          var projection = layer.projection;
768

    
769
          if(!projection) {
770
            projection = layerProjections[layer.name];
771
          }
772

    
773

    
774
          if(projection) {
775
            callback(projection);
776
          } else {
777
            // asking the edit map server would be the best:
778
            //    > http://edit.africamuseum.be/geoserver/topp/ows?service=WFS&version=1.0.0&request=GetFeature&typeName=topp:euromed_2013&maxFeatures=1&outputFormat=application/json
779
            // or
780
            //    > http://edit.africamuseum.be/geoserver/topp/ows?service=WFS&request=getCapabilities'
781
            // but the latter returns only XML
782
            var parameters = {
783
              service: 'WFS',
784
              version: '1.0.0',
785
              request: 'GetFeature',
786
              typeName: layer.name,
787
              maxFeatures: 1, // only one feature
788
              outputFormat: 'text/javascript',
789
              format_options: 'callback:getJson'
790
            };
791

    
792
            jQuery.ajax({
793
              url: opts.wfsRootUrl + "?" + jQuery.param(parameters),
794
              dataType: 'jsonp',
795
              jsonpCallback: 'getJson',
796
              success: function(data, textStatus, jqXHR){
797
                if(data.crs && data.crs.type && data.crs.properties.code){
798
                  var projectionName = data.crs.type + "_" + data.crs.properties.code;
799
                  log("projection name found in WFS response:" + projectionName);
800
                  projection = CdmOpenLayers.projections[projectionName.toLowerCase()];
801
                  callback(projection);
802
                }
803
              },
804
              error : function(jqXHR, textStatus, errorThrown) {
805
                log("projection name not found in WFS response, due to error: " + textStatus);
806
                projection = CdmOpenLayers.projections.epsg_4326;
807
                callback(projection);
808
              }
809

    
810
            });
811
          }
812
        };
813

    
814
        /**
815
         *
816
         */
817
        var addLegendAsElement= function(legendSrcUrl){
818

    
819
            var legendElement = jQuery('<div class="openlayers_legend"></div>');
820
            var legendImage = jQuery('<img src="' + legendSrcUrl + '"/>');
821
            legendElement
822
                .css('opacity', opts.legendOpacity)
823
                .css('position', 'relative')
824
                .css('z-index', '1002')
825
                .css('top', -mapElement.height());
826
            legendImage.load(function () {
827
                jQuery(this).parent()
828
                    .css('left', getWidth() - jQuery(this).width())
829
                    .width(jQuery(this).width());
830
                // reset height to original value
831
                adjustHeight();
832
            });
833
            legendElement.html(legendImage);
834
            mapElement.after(legendElement);
835
        };
836

    
837
         var adjustLegendAsElementPosition = function (){
838
           var legendContainer = mapContainerElement.children('.openlayers_legend');
839
           var legendImage = legendContainer.children('img');
840
           legendContainer.css('top', -mapElement.height())
841
             .css('left', getWidth() - legendImage.width());
842
         };
843

    
844

    
845
        var addLegendAsLayer= function(legendSrcUrl, map){
846
            var w, h;
847

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

    
852
                w = mapElement.next('.openlayers_legend').find('img').width();
853
                h = mapElement.next('.openlayers_legend').find('img').height();
854
                mapElement.next('.openlayers_legend').remove();
855

    
856
//              createLegendLayer();
857
//              // 2. create the Legend Layer
858
                //TODO createLegendLayer as inner function seems like an error
859
//              var createLegendLayer = function(){
860
                //
861
                //
862
//              var legendLayerOptions={
863
//              maxResolution: '.$maxRes.',
864
//              maxExtent: new OpenLayers.Bounds(0, 0, w, h)
865
//              };
866
                //
867
//              var legendLayer = new OpenLayers.Layer.Image(
868
//              'Legend',
869
//              legendSrcUrl,
870
//              new OpenLayers.Bounds(0, 0, w, h),
871
//              new OpenLayers.Size(w, h),
872
//              imageLayerOptions);
873
//              };
874
            });
875

    
876

    
877
        };
878

    
879
        /**
880
         * merge 2 Url query strings
881
         */
882
        var mergeQueryStrings = function(queryStr1, queryStr2){
883
            if(queryStr1.charAt(queryStr1.length - 1) != '&'){
884
                queryStr1 += '&';
885
            }
886
            if(queryStr2.charAt(0) == '&'){
887
                return queryStr1 + queryStr2.substr(1);
888
            } else {
889
                return queryStr1 + queryStr2;
890
            }
891

    
892
        };
893

    
894
        /**
895
         *
896
         */
897
        var createBaseLayers = function( baseLayerNames, defaultBaseLayerName, customWMSBaseLayerData){
898

    
899
            for(var i = 0; i <  baseLayerNames.length; i++) {
900
                // create the layer
901
                if (baseLayerNames[i] === "custom_wms_base_layer_1"){
902
                    wmsBaseLayer =createWMSLayer(
903
                            customWMSBaseLayerData.name,
904
                            customWMSBaseLayerData.url,
905
                            customWMSBaseLayerData.params,
906
                            customWMSBaseLayerData.projection,
907
                            customWMSBaseLayerData.proj4js_def,
908
                            customWMSBaseLayerData.units,
909
                            customWMSBaseLayerData.max_extent,
910
                            customWMSBaseLayerData.untiled
911
                    );
912
                  wmsBaseLayer.setIsBaseLayer(true);
913
                  baseLayers[i] = wmsBaseLayer;
914
                } else {
915
                    baseLayers[i] = window.CdmOpenLayers.getLayerByName(baseLayerNames[i]);
916
                }
917
                // set default baselayer
918
                if(baseLayerNames[i] === defaultBaseLayerName){
919
                    defaultBaseLayer = baseLayers[i];
920
                }
921

    
922
            }
923
        };
924

    
925
        /**
926
         * returns the intersection of the bounds b1 and b2.
927
         * The b1 and b2 do not intersect b1 will be returned.
928
         *
929
         * @param OpenLayers.Bounds b1
930
         * @param OpenLayers.Bounds b2
931
         *
932
         * @return the bounds of the intersection between both rectangles
933
         */
934
        var intersectionOfBounds = function (b1, b2){
935

    
936
            if(b1.intersectsBounds(b2)){
937

    
938
                var left = Math.max(b1.left, b2.left);
939
                var bottom = Math.max(b1.bottom, b2.bottom);
940
                var right = Math.min(b1.right, b2.right);
941
                var top = Math.min(b1.top, b2.top);
942

    
943
                return new OpenLayers.Bounds(left, bottom, right, top);
944

    
945
            } else {
946
                return b1;
947
            }
948
        };
949

    
950
        /**
951
         *
952
         * @param b OpenLayers.Bounds to crop
953
         * @param aspectRatio as fraction of width/height as float value
954
         *
955
         * @return the bounds cropped to the given aspectRatio
956
         */
957
        var cropBoundsToAspectRatio = function (b, aspectRatio){
958

    
959
            var cropedB = b.clone();
960

    
961
            if(aspectRatio === 1){
962
                return cropedB;
963
            }
964

    
965
            /*
966
             * LonLat:
967
             *   lon {Float} The x-axis coordinate in map units
968
             *   lat {Float} The y-axis coordinate in map units
969
             */
970
            var center = cropedB.getCenterLonLat();
971
            var dist;
972
            if(aspectRatio < 1){
973
                dist = (b.getHeight() / 2) * aspectRatio;
974
                cropedB.top = center.lat + dist;
975
                cropedB.cropedBottom = center.lat - dist;
976
            } else if(aspectRatio > 1){
977
                dist = (b.getWidth() / 2) / aspectRatio;
978
                cropedB.left = center.lon - dist;
979
                cropedB.right = center.lon + dist;
980
            }
981
            return cropedB;
982
        };
983

    
984
        /**
985
         * returns the version number contained in the version string:
986
         *   v1.1 --> 1.1
987
         *   v1.2_dev --> 1.2
988
         */
989
        var mapServerVersionNumber = function() {
990
            var pattern = /v([\d\.]+).*$/;
991
            var result;
992
            if (result = mapserverVersion.match(pattern) !== null) {
993
                return result[0];
994
            } else {
995
                return null;
996
            }
997
        };
998

    
999

    
1000

    
1001
      /**
1002
       * returns the zoom to bounds.
1003
       *
1004
       * @param bboxString
1005
       *     a string representation of the bounds in degree for epsg_4326
1006
       *
1007
       * @return the bboxstring projected onto the layer and intersected with the maximum extent of the layer
1008
       */
1009
      var calculateZoomToBounds = function(bboxString){
1010
        var zoomToBounds;
1011
        if(bboxString) {
1012
          zoomToBounds = OpenLayers.Bounds.fromString(bboxString);
1013
          if(referenceProjection().proj.projName){
1014
            // SpericalMercator is not supporting the full extent -180,-90,180,90
1015
            // crop if need to -179, -85, 179, 85
1016
            if(zoomToBounds.left < -179){
1017
              zoomToBounds.left =  -179;
1018
            }
1019
            if(zoomToBounds.bottom < -85){
1020
              zoomToBounds.bottom =  -85;
1021
            }
1022
            if(zoomToBounds.right > 179){
1023
              zoomToBounds.right =  179;
1024
            }
1025
            if(zoomToBounds.top > 85){
1026
              zoomToBounds.top = 85;
1027
            }
1028
          }
1029
          // transform bounding box given in degree values to the projection of the base layer
1030
          zoomToBounds.transform(CdmOpenLayers.projections.epsg_4326, referenceProjection());
1031
        } else if(referenceMaxExtent()) {
1032
          zoomToBounds = referenceMaxExtent();
1033
          // no need to transform since the bounds are obtained from the layer
1034
        } else {
1035
          // use the more narrow bbox of the SphericalMercator to avoid reprojection problems
1036
          // SpericalMercator is not supporting the full extent!
1037
          zoomToBounds = CdmOpenLayers.mapExtends.epsg_900913;
1038
          // transform bounding box given in degree values to the projection of the base layer
1039
          zoomToBounds.transform(CdmOpenLayers.projections.epsg_4326, referenceProjection());
1040
        }
1041

    
1042
        zoomToBounds = intersectionOfBounds(referenceMaxExtent(), zoomToBounds);
1043

    
1044
        log("zoomBounds calculated: " + zoomToBounds.toString());
1045

    
1046
        return zoomToBounds;
1047
      };
1048

    
1049
      var log = function(message, addTimeStamp){
1050
        var timestamp = '';
1051
        if(addTimeStamp === true){
1052
          var time = new Date();
1053
          timestamp = time.getSeconds() + '.' + time.getMilliseconds() + 's';
1054
        }
1055
        console.log(timestamp + message);
1056
      };
1057

    
1058
      var makeWMSLayerOptions = function(projection, proj4js_def, maxExtent, units, untiled) {
1059
        var wmsOptions = {
1060
          isBaseLayer: false,
1061
          displayInLayerSwitcher: true
1062
        };
1063

    
1064
        if (projection) {
1065
          if (proj4js_def) {
1066
            // in case projection has been defined for the layer and if there is also
1067
            // a Proj4js.defs, add it!
1068
            Proj4js.defs[projection] = proj4js_def;
1069
          }
1070
          wmsOptions.projection = projection;
1071
          if (maxExtent === null) {
1072
            maxExtent = CdmOpenLayers.mapExtends.epsg_4326.clone();
1073
            maxExtent.transform(CdmOpenLayers.projections.epsg_4326, projection);
1074
          }
1075
        } else {
1076
          // use the projection and maxextent of the base layer
1077
          maxExtent = referenceMaxExtent();
1078
        }
1079

    
1080
        if (maxExtent) {
1081
          wmsOptions.maxExtent = maxExtent;
1082
        }
1083

    
1084
        if (units) {
1085
          wmsOptions.units = units;
1086
        }
1087

    
1088
        if (untiled) {
1089
          wmsOptions.singleTile = true;
1090
          wmsOptions.ratio = 1;
1091
        }
1092

    
1093
        return wmsOptions;
1094
      };
1095

    
1096
      /**
1097
       * Creates a WMS Base layer
1098
       * @param String name
1099
       *     A name for the layer
1100
       * @param String url
1101
       *     Base url for the WMS (e.g.  http://wms.jpl.nasa.gov/wms.cgi)
1102
       * @param Object params
1103
       *     An object with key/value pairs representing the GetMap query string parameters and parameter values.
1104
       * @param Object projection
1105
       *    A OpenLayers.Projection object
1106
       */
1107
      var createWMSLayer= function(name, url, params, projection, proj4js_def, units, maxExtent, untiled){
1108

    
1109
        console.log("creating WMS Layer " + name);
1110

    
1111
        var wmsOptions = makeWMSLayerOptions(projection, proj4js_def, maxExtent, units, untiled);
1112

    
1113
        var wmsLayer = new OpenLayers.Layer.WMS(
1114
            name,
1115
            url,
1116
            params,
1117
            wmsOptions
1118
          );
1119

    
1120
          if(wmsLayer === null){
1121
            console.log("Error creating WMS Layer");
1122
          }
1123

    
1124
          return  wmsLayer;
1125
        };
1126

    
1127
        var layerLoadingControl = function() {
1128

    
1129
          var control = new OpenLayers.Control();
1130

    
1131
          OpenLayers.Util.extend(control, {
1132

    
1133
            LAYERS_LOADING: 0,
1134

    
1135
            updateState: function () {
1136
              if(this.div != null){
1137
                if (this.LAYERS_LOADING > 0) {
1138
                  this.div.style.display = "block";
1139
                } else {
1140
                  this.div.style.display = "none";
1141
                }
1142
              }
1143
            },
1144

    
1145
            updateSize: function () {
1146
              this.div.style.width = this.map.size.w + "px";
1147
              this.div.style.height = this.map.size.h  + "px";
1148
              this.div.style.textAlign = "center";
1149
              this.div.style.lineHeight = this.map.size.h  + "px";
1150
            },
1151

    
1152
            counterIncrease: function (layer) {
1153
              this.control.LAYERS_LOADING++;
1154
              log(' > loading start : ' + this.layer.name + ' ' + this.control.LAYERS_LOADING, true);
1155
              this.control.updateState();
1156
            },
1157

    
1158
            counterDecrease: function (layer) {
1159
              this.control.LAYERS_LOADING--;
1160
              log(' > loading end : ' + this.layer.name + ' ' + this.control.LAYERS_LOADING, true);
1161
              this.control.updateState();
1162
            },
1163

    
1164
            draw: function () {
1165

    
1166
              // call the default draw function to initialize this.div
1167
              OpenLayers.Control.prototype.draw.apply(this, arguments);
1168

    
1169
              this.map.events.register('updatesize', this, function(e){
1170
                  this.updateSize();
1171
                }
1172
              );
1173

    
1174
              var loadingIcon = document.createElement("i");
1175
              var fa_class = document.createAttribute("class");
1176
              // fa-circle-o-notch fa-spin
1177
              // fa-spinner fa-pulse
1178
              // fa-refresh
1179
              fa_class.value = "fa fa-refresh fa-spin fa-5x";
1180
              loadingIcon.attributes.setNamedItem(fa_class);
1181

    
1182
              this.updateSize();
1183

    
1184
              this.div.appendChild(loadingIcon);
1185

    
1186
              this.registerEvents();
1187

    
1188
              return this.div;
1189
            },
1190

    
1191
            registerEvents: function() {
1192

    
1193
              this.map.events.register('preaddlayer', this, function(e){
1194
                console.log(" > preaddlayer " + e.layer.name);
1195
                e.layer.events.register('loadstart', {control: this, layer: e.layer}, this.counterIncrease);
1196
                e.layer.events.register('loadend', {control: this, layer: e.layer}, this.counterDecrease);
1197
              });
1198
            }
1199

    
1200
          });
1201

    
1202
          return control;
1203
        }
1204

    
1205
    }; // end of CdmOpenLayers.Map
1206
})();
1207

    
1208

    
1209

    
1210

    
1211

    
1212

    
1213

    
1214

    
1215

    
(2-2/2)