Project

General

Profile

1
/*
2
Author:       Mike Adair mike.adairATccrs.nrcan.gc.ca
3
License: LGPL as per: http://www.gnu.org/copyleft/lesser.html
4
$Id: Extent.js 3067 2007-08-03 09:25:59Z jseb.baklouti $
5
*/
6

    
7

    
8
var Rearth = 6378137.0;                 // Radius of the earth (sphere); different from Proj value?
9
var degToMeter = Rearth*2*Math.PI/360;
10
//var mbScaleFactor = 72 * 39.3701;   //PixelsPerInch*InchesPerMapUnit; magic numbers 
11
                                    //need to determine magic number for lat/lon
12
var mbScaleFactor = 3571.428;   //magic number, for Geoserver SLD compatibility
13
                               // 1/0.00028 (0.28 mm "is a common actual size for
14
                               // contemporary display" as written in the SLD specification ...
15

    
16
/*
17
 * FD 2005/03/04 : minScale et maxScale
18
 * DGR : should be in config ?
19
 */
20
var minScale = 1000;
21
var maxScale = 200000;
22

    
23
/**
24
 * A tool designed to handle geography calculations for widgets which render
25
 * the model in 2D.
26
 * Use of this tool requires that it's model implements get/setWindowHeight/Width
27
 * methods.
28
 * Encapsulates all geography and image size aspects of a geographic object 
29
 * displayed in a rectangular area on the screen.
30
 * All coordinates are handled as points which is a 2 element array, where x is 
31
 * the first element and y is the second. Coordinates are either pixel and lixel
32
 * (pl) relative to the top left of the extent or projection XY values (xy). 
33
 *
34
 * @constructor
35
 * @param model       the model document that this extent represents
36
 * @param initialRes  (optional) if supplied the extent resolution will be set to this value
37
 */
38
function Extent( model, initialRes ) {
39
  this.model = model;
40
  this.id = model.id + "_MbExtent" + mbIds.getId();
41
  this.size = new Array();
42
  this.res = new Array();
43
  this.zoomBy = 4;
44
  
45
  /**
46
   * Returns the bounding box as stored in the model
47
   * @return array with the bounding box
48
   */
49
  this.getBbox = function() {
50
    var bbox = this.model.getBoundingBox();
51
    return bbox;
52
  }
53
  
54
  /**
55
   * Recalculates a given bbox and stores a proper aspect one in the model
56
   * @param bbox  an array with a bounding box
57
   */
58
  this.setBbox = function(bbox){
59
    size = this.getSize();
60
    res = Math.max((bbox[2] - bbox[0])/size[0], (bbox[3] - bbox[1])/size[1]);
61
    scale=this.getFixedScale(res);
62
    center = new Array((bbox[1]-bbox[3])/2,(bbox[0]-bbox[2])/2);//center=horizontal,vertical
63
    half = new Array(size[0]/2,size[1]/2);
64
    bbox = new Array(center[0]-half[0]*scale, center[1]-half[1]*scale, center[0]+half[0]*scale,center[1]+half[1]*scale);
65
    this.model.setBoundingBox(bbox);
66
  }
67
  
68
   /**
69
   * Returns the window size as stored in the model
70
   * @return array with the window size
71
   */
72
  this.getSize = function() {
73
    size= new Array();
74
    size[0] = this.model.getWindowWidth();
75
    size[1] = this.model.getWindowHeight();
76
    return size;
77
  }
78
  
79
   /**
80
   * Stores a given window size in the model.
81
   * Can be used in the future for dynamic window resizing
82
   * @param size  an array with a window size
83
   */
84
  this.setSize = function(size){
85
    this.model.setWindowWidth(size[0]);
86
    this.model.setWindowHeight(size[1]);
87
  }
88
  
89
   /**
90
   * When given a res, it recalculates it to match the zoomlevels, when present and returns a fixed scale.
91
   * When no res is given it returns the maximum resolution
92
   * @param res optional resolution to be checked
93
   * @return fixedScale the resolution to display the map with
94
   */
95
  this.getFixedScale = function(res) {
96
  if (this.zoomLevels){
97
    if (!res) {
98
      this.setResolution( new Array(this.model.getWindowWidth(), this.model.getWindowHeight() ) );
99
      res = Math.max(this.res[0],this.res[1]);
100
     
101
    }
102
    var sortstring = "function sort(a,b){return b-a}";
103
    var evalsort= eval(sortstring);
104
    var zoomLevels = this.zoomLevels.sort(evalsort);
105
    var i=0;
106
    while(zoomLevels[i] >= res){
107
      i++;
108
    }
109
    if(i==0) {
110
    i=1;
111
    }
112
    this.fixedScale = zoomLevels[i-1];
113
    }
114
    else this.fixedScale = Math.max(this.res[0],this.res[1]);
115
    return this.fixedScale;
116
    
117
  }
118
  
119
  /* 
120
   * Sets the zoomLevels in the extent
121
   * @param enabled boolean to enable or disable zoomLevels support
122
   * @param zoomLevels an array containing a fixed set of zoomLevels
123
   */
124
  this.setZoomLevels = function(enabled,zoomLevels){
125
    if(enabled) {
126
      this.zoomLevels = zoomLevels;
127
    }
128
    else this.zoomLevels = null;
129
  }
130
 
131
  /*
132
   * Recalculates the lr and ul to a proper aspect. It also takes into account zoomLevels when present.
133
   */
134
  this.checkBbox = function() {
135
    var center = this.getCenter();
136
    var half = new Array(this.size[0]/2, this.size[1]/2);
137
    var res = this.getFixedScale();
138
    this.lr = new Array(center[0]+half[0]*res, center[1]-half[1]*res);
139
    this.ul = new Array(center[0]-half[0]*res, center[1]+half[1]*res);
140
  }
141
  /**
142
   * Returns the XY center of this extent
143
   * @return  array of XY for th center of the extent
144
   */
145
  this.getCenter = function() {
146
    return new Array((this.ul[0]+this.lr[0])/2, (this.ul[1]+this.lr[1])/2);
147
  }
148

    
149
  /**
150
   * Returns XY coordinates for given pixel line coords w.r.t. top left corner
151
   * @param pl   pixel line in extent to calculate
152
   * @return     point array of XY coordinates
153
   */
154
  this.getXY = function(pl) {
155
    //switch(this.model.getSRS()) {
156
    //  case "EPSG:GMAPS":       //@TODO Cleanup this hack
157
    //    gmap=this.model.getParam("gmap");
158
    //    if(gmap){
159
    //      p=new GPoint(pl[0],pl[1]);
160
    //      latlng=gmap.fromDivPixelToLatLng(p);
161
    //      latlng=new Array(latlng.lng(),latlng.lat());
162
    //    }
163
    //    else alert("Extent: gmap not defined");
164
    //    break;
165
    //  default:
166
    //    latlng=new Array(this.ul[0]+pl[0]*this.res[0],this.ul[1]- pl[1]*this.res[1]);
167
    //    break;
168
    //}
169
    latlng=new Array(this.ul[0]+pl[0]*this.res[0],this.ul[1]- pl[1]*this.res[1]);
170
    return latlng;
171
  }
172

    
173
  /**
174
   * Returns pixel/line coordinates for given XY projection coords
175
   * @param xy   projection XY coordinate to calculate
176
   * @return     point array of pxiel/line coordinates w.r.t. top left corner
177
   */
178
  this.getPL = function(xy) {
179
    //switch(this.model.getSRS()) {
180
    //  case "EPSG:GMAPS":       //@TODO Cleanup this hack
181
    //    return xy;
182
    //}
183
    
184
    var p = Math.floor( (xy[0]-this.ul[0])/this.res[0] );
185
    var l = Math.floor( (this.ul[1]-xy[1])/this.res[1] );
186
    return new Array(p,l);
187
  }
188

    
189
  /**
190
   * Adjust the extent so that it is centered at given XY coordinate with given
191
   * resolution.  Extent width and height remain fixed.  Optionally check to 
192
   * ensure that it doesn't go beyond available extent.
193
   *
194
   * @param center      projection XY coordinate to center at
195
   * @param newres      resolution to display at
196
   * @param limitExtent ensure that the extent doesn't go beyond available bbox (TBD: not complete/tested)
197
   * @return            none
198
   */
199
  this.centerAt = function(center, newres, limitExtent) {
200
    var half = new Array(this.size[0]/2, this.size[1]/2);
201
/*
202
 * FD 2005/03/04 : respect de minScale et maxScale
203
 * DGR : scale constraints
204
    var nRmin= minScale/mbScaleFactor;
205
    var nRmax= maxScale/mbScaleFactor;
206
    if (newres < nRmin) {
207
      newres= nRmin ;
208
    }
209
    if (newres > nRmax) {
210
      newres= nRmax ;
211
    }
212
 */
213
    if (this.zoomLevels) {
214
      newres=this.getFixedScale(newres);
215
    }
216
    this.lr = new Array(center[0]+half[0]*newres, center[1]-half[1]*newres);
217
    this.ul = new Array(center[0]-half[0]*newres, center[1]+half[1]*newres);
218

    
219
    //make sure the request doesn't extend beyond the available model
220
    //TBD this block not tested
221
    if ( limitExtent ) {
222
      var xShift = 0;
223
      if ( this.lr[0] > ContextExtent.lr[0] ) xShift = ContextExtent.lr[0] - this.lr[0];
224
      if ( this.ul[0] < ContextExtent.ul[0] ) xShift = ContextExtent.ul[0] - this.ul[0];
225
      this.lr[0] += xShift;
226
      this.ul[0] += xShift;
227

    
228
      var yShift = 0;
229
      if ( this.lr[1] < ContextExtent.lr[1] ) yShift = ContextExtent.lr[1] - this.lr[1];
230
      if ( this.ul[1] > ContextExtent.ul[1] ) yShift = ContextExtent.ul[1] - this.ul[1];
231
      this.lr[1] += yShift;
232
      this.ul[1] += yShift;
233
    }
234

    
235
    this.model.setBoundingBox( new Array(this.ul[0], this.lr[1], this.lr[0], this.ul[1]) );
236
    //this.setResolution(size);
237
    this.setSize(newres);
238
  }
239

    
240
  /**
241
   * Adjust the extent to the given bbox.  Resolution is recalculated. 
242
   * Extent width and height remain fixed.  
243
   * @param ul      upper left coordinate of bbox in XY projection coords
244
   * @param lr      lower right coordinate of bbox in XY projection coords
245
   */
246
  this.zoomToBox = function(ul, lr) {    //pass in xy
247
    var center = new Array((ul[0]+lr[0])/2, (ul[1]+lr[1])/2);
248
    newres = Math.max((lr[0] - ul[0])/this.size[0], (ul[1] - lr[1])/this.size[1]);
249
    this.centerAt( center, newres );
250
  } 
251

    
252
/**
253
   * Adjust the width and height to that bbox is displayed at specified resolution
254
   * @param res   the resolution to be set
255
   */
256
  //TBD update the model doc
257
  this.setSize = function(res) {     //pass in a resolution and width, height are recalculated
258
    this.res[0] = this.res[1] = res;
259
    this.size[0] = (this.lr[0] - this.ul[0])/this.res[0];
260
    this.size[1] = (this.ul[1] - this.lr[1])/this.res[1];
261
    this.width = Math.ceil(this.size[0]);
262
    this.height = Math.ceil(this.size[1]);
263
  }
264

    
265
  /**
266
   * Adjust the resolution so the bbox fits in the specified width and height
267
   * @param size   width, height array passed in
268
   */
269
  //TBD update the model doc
270
  this.setResolution = function(size) {    //pass in a width, height and res is recalculated
271
    this.size[0] = size[0];
272
    this.size[1] = size[1];
273
    this.res[0] = (this.lr[0] - this.ul[0])/this.size[0];
274
    this.res[1] = (this.ul[1] - this.lr[1])/this.size[1];
275
    this.width = Math.ceil(this.size[0]);
276
    this.height = Math.ceil(this.size[1]);
277
  }
278

    
279
  /**
280
   * Returns the map scale denominator for the current extent resolution
281
   * @return map scale denominator
282
   */
283
  this.getScale = function() {
284
    var pixRes = null;
285
    switch(this.model.getSRS()) {
286
      case "EPSG:GMAPS":
287
        break;
288
      case "EPSG:4326":				//all projection codes in degrees
289
      case "EPSG:4269":				
290
        pixRes = this.res[0]*degToMeter;
291
        break;
292
      default:                //all projection codes in meters
293
        pixRes = this.res[0];
294
        break;
295
    }
296
    return mbScaleFactor*pixRes;
297
  }
298

    
299
  /**
300
   * Sets the model's resolution from mapScale input value.  The map center 
301
   * remains fixed.
302
   * @param scale   map scale denominator value
303
   */
304
  this.setScale = function(scale) {
305
    var newRes = null;
306
/*
307
 * FD 2005/03/04
308
 * On contraint l'echelle min et max de l'application.
309
 * A externaliser dans le fichier de config de l'application ;-)
310
 * DGR : should be in the config
311
    if (scale < minScale) {
312
      scale= minScale ;
313
    }
314
    if (scale > maxScale) {
315
      scale= maxScale ;
316
    }
317
 */
318
    switch(this.model.getSRS()) {
319
      case "EPSG:4326":				//all projection codes in degrees
320
      case "EPSG:4269":				
321
        //convert to resolution in degrees
322
        newRes = scale/(mbScaleFactor*degToMeter);
323
        break;
324
      default:                //all projection codes in meters
325
        newRes = scale/mbScaleFactor;
326
        break;
327
    }
328
    this.centerAt(this.getCenter(), newRes );
329
  }
330

    
331

    
332
  /**
333
   * Initialization of the Extent tool, called as a loadModel event listener.
334
   * @param extent      the object being initialized
335
   * @param initialRes  (optional) if supplied the extent resolution will be set to this value
336
   */
337
  this.init = function(extent, initialRes) {
338
    var bbox = extent.model.getBoundingBox();
339
    extent.ul = new Array(bbox[0],bbox[3]);
340
    extent.lr = new Array(bbox[2],bbox[1]);
341
/*
342
TBD: when called as a listener this gets a bbox array passed in, not initialRes value
343
    if ( initialRes ) {
344
      extent.setSize( initialRes );
345
    } else {
346
      extent.setResolution( new Array(extent.model.getWindowWidth(), extent.model.getWindowHeight() ) );
347
    }
348
*/
349
    extent.setResolution( new Array(extent.model.getWindowWidth(), extent.model.getWindowHeight() ) );
350
    extent.checkBbox();
351
  }
352
  if ( initialRes ) this.init(this, initialRes);
353

    
354

    
355
  this.firstInit = function(extent, initialRes) {
356
  	extent.init(extent, initialRes);
357
    //TBD: this causes 2 paint() calls on initial load, not sure why it's here - MA
358
	  //extent.zoomToBox(extent.ul,extent.lr);
359
  }
360

    
361
}
362

    
363
  
364
  
(5-5/15)