Fanning Software Consulting

UV_Box Ranges Are Incorrect in Map_Proj_Init

QUESTION: It appears to me that the UV ranges (also known as the projected XY range) returned in the uv_box field of map structure created by Map_Proj_Init are incorrect. For example, consider this.

   IDL> mapStruct = Map_Proj_Init('Equirectangular', Center_Longitude=90.0)
   IDL> Print, mapStruct.uv_box[[0,2]]
        -20015077.       18903129.

These values should be exactly the same, with opposite signs. In fact, since I am specifying a longitude range of -180 to 180 (by not using a LIMIT keyword), they should map to the same point. But they don't. Consider this.

   IDL> ll = Map_Proj_Inverse(mapStruct.uv_box[[0,2]], mapStruct.uv_box[[1,3]], $
   IDL> Print, ll[0,0], ll[0,1]
         -90.000000      -100.00000

You see the values are off by 10 degrees.

ANSWER: Yes, this appears to be a bug in Map_Proj_Init when the center longitude is any value other than 0.0. (See another problem with the uv_box ranges in the next question below.) You can see the problem by running this example code.

    FOR j=0,10 DO BEGIN
       center_lon = 0 > j*36.0d < 360.0
       mapStruct = Map_Proj_Init(117, CENTER_LONGITUDE=center_lon)
       Print, center_lon, mapStruct.uv_box[[0,2]]
       ll = Map_Proj_Inverse(mapStruct.uv_box[[0,2]], $
              mapStruct.uv_box[[1,3]], MAP_STRUCTURE=mapStruct)
       Print, center_lon, ll[0,0], ll[0,1]
       Print, ""

Here are some representative results.

      0.00000000      -20015077.       20015077.
      0.00000000       180.00000      -180.00000

       108.00000      -19792688.       19125518.
       108.00000      -70.000000      -80.000000

       180.00000      -20015077.       18903129.
       180.00000      0.00000000      -10.000000

       252.00000      -19125518.       19792688.
       252.00000       80.000000       70.000000

       360.00000      -20015077.       20015066.
       360.00000       180.00000       179.99990

You can see that the results are only correct when the center longitude is equal to zero.

This problem is corrected in the Coyote Graphics map projection routine, cgMap, which is a smart wrapper for the Map_Proj_Init command.

QUESTION: But, I think the uv_box is wrong even if I use a LIMIT keyword. If, as the IDL documentation states, the uv_box of the map structure "contains the map range in meters", then surely this is coincident with the limits of the map projection. But, it doesn't appear to be. Consider this example.

   limit =  [42.4000d, -92.9000d, 47.1000d, -86.8000d]
   mapStruct = Map_Proj_Init('Albers Equal Area', /GCTP, $
      Limit=limit, Standard_Par1=40, Standard_Par2=-39, Ellipsoid=8)
   xy = Map_Proj_Forward(limit[[1,3]], limit[[0,2]], MAP_STRUCTURE=mapStruct)
   Print, 'X Range: ', mapStruct.uv_box[[0,2]]
   Print, 'X Range: ', Reform(xy[0,*])
   Print, 'Y Range: ', mapStruct.uv_box[[1,3]]
   Print, 'Y Range: ', Reform(xy[1,*])

Here are the results. You can see clearly that the uv_box results are different from the projected limit results in the latitude values.

   X Range:       -7930061.5      -7404415.9
   X Range:       -7930061.5      -7404415.9

   Y Range:        5598899.5       6088932.0
   Y Range:        5604417.8       6083417.4

ANSWER: Yes, I think this incorrect, too. You can confirm this by inverse projecting the uv_box values back to latitude/longitude values.

   ll = Map_Proj_Inverse(mapStruct.uv_box[[0,2]], mapStruct.uv_box[[1,3]], MAP_STRUCTURE=mapStruct)
   Print, 'Lons: ', Reform(ll[0,*])
   Print, 'Lats: ', Reform(ll[1,*])

Here are the results.

   Lons:       -92.899293      -86.800663
   Lats:        42.348513       47.155763

Clearly, the latitude values are wrong by a significant amount.

In the cgMap object, I assume that the forward map projection code is working correctly (or else all is lost!) and I correct the map structure's uv_box field to correspond to the map limit. Here is an example.

   limit =  [42.4000d, -92.9000d, 47.1000d, -86.8000d]
   map = Obj_New('cgMap', 'Albers Equal Area', /GCTP, $
      Limit=limit, Standard_Par1=40, Standard_Par2=-39, Ellipsoid=8)
   s = map -> GetMapStruct()
   Print, 'X Range: ', s.uv_box[[0,2]]
   Print, 'Y Range: ', s.uv_box[[1,3]]

And, here are the results.

   X Range:       -7930061.4      -7404415.8
   Y Range:        5604418.1       6083417.7

And, when back projected:

   ll = map -> Inverse(s.uv_box[[0,2]], s.uv_box[[1,3]])
   Print, 'Lons: ', Reform(ll[0,*])
   Print, 'Lats: ', Reform(ll[1,*])

You get this:

   Lons:       -92.900000      -86.800000
   Lats:        42.400000       47.100000

Version of IDL used to prepare this article: IDL 7.1.2.

Written: 30 November 2011
Updated: 11 April 2012