Using the "C API" and modifying the projection...

Ian Erickson ierickson at ANALYGIS.COM
Thu Jun 21 11:29:39 EDT 2007


I know, I know....

The 'c', 'o', 'u', and 't' keys are a little worse for wear on the 'ol 
keyboard.  Although...I seem to have found my issue.

Rule #1)  Make sure that the import libraries that you're linking 
against are the same DLLs that you're running with.

I had an evironment variable (for PHP) that was pointing my application 
to DLLs of version 4.10.0 (not the 4.10.2 DLLs that I was building 
against). 

Sorry for the fire alarm everyone, but after a day of trying it's 
finally working!!  For the record, this is the code that does actually 
work when compiled, linked....and run correctly!

//--------------------------------------
   mapObj *map = NULL;
   imageObj *image = NULL;

   msSetup();
   map = msLoadMap( "C:\\original.map", NULL );
   if (!map) {
       cout << "There is an error loading the map..." << endl;
       return;
   }
   //Trying to change the projection to epsg:4269 (and then verify it in 
the output.map file)...
   msLoadProjectionString( &(map->projection), "+init=epsg:4269" );
   msSaveMap( map, "C:\\output.map" );
     image = msDrawMap( map );
   msSaveImage( map, image, "C:\\output.png" );

   msFreeImage( image );
   msFreeMap( map );
   msCleanup();
//--------------------------------------

Thanks!


Daniel Morissette wrote:

>
> Wheeeeewwwweeewww... all those "cout << ... << ... << ..."  make my 
> head spin counterclockwise!  ;) ;)
>
> If my internal interpreter recovered from all that spinning and ran 
> your code correctly, I suspect your problem may be related to the way 
> you try to make a copy of map->projection in 'in':
>
>    msInitProjection( &in );
>    in = map->projection;
>
> Since this is a C API and not C++ classes, assigning 
> in=map->projection like this is asking for trouble. The projectionObj 
> contains some pointers to allocated buffers and/or data structures 
> that won't be copied by this assignment so you end up with a half 
> copy. I think it would be safer to use a "projectionObj *in" which 
> would make it clear that you are just carrying a ref to 
> map->projection and not trying to make a copy... or just don't bother 
> using 'in' and use map->projection explicitly everywhere.
>
> I think what may be happening here is that the call to 
> msLoadProjectionString(&map->projection,msGetProjectionString(projOut)); 
>  later on overwrites some of the contents of 'in' which are really 
> data structures owned by map->projection and not by 'in'.
>
> I suspect that moving the call to 
> msLoadProjectionString(&map->projection,msGetProjectionString(projOut)); 
> to a line after you have reprojected the extents might resolve the 
> issue. But the correct fix would be to not try to make that half-copy 
> of map->projection in 'in' as I explained above.
>
> Of course I could be plain wrong on this... my head is still 
> spinning... wheeewweew  << ewwww << eewww << ... ;)
>
> Daniel
>
>
> Ian Erickson wrote:
>
>> Gentlemen,
>>
>> I know that the C API was never really intended for use directly as a 
>> developer's platform, although when evaluating all of my options it 
>> was clear that what I needed to do was simple and the C API (map.h, 
>> mapproject.h) were ideal for my situation....however....
>>
>> I've put together a simple console application to test the API and 
>> (whatever I try) I cannot get the map to change projections and 
>> specify the extent of the new map in projected coordinates.
>>
>> Here's an extract...
>> //---------------------------------------------------------------------------- 
>>
>> void simpleMap( double minx = -180.0, double miny = -90.0, double 
>> maxx = 180.0, double maxy = 90.0 )
>> {
>>    mapObj *map = NULL;
>>    imageObj *image = NULL;
>>
>>    // This map and every layer in it has the projection of epsg:4326...
>>    map = msLoadMap("C:\\some.map", NULL);
>>
>>    pointObj *pt_ll = pointObj_new();
>>    pointObj *pt_ur = pointObj_new();
>>
>>    // *** Assign the coordinates to be reprojected.
>>    pt_ll->x = minx;
>>    pt_ll->y = miny;
>>    pt_ur->x = maxx;
>>    pt_ur->y = maxy;
>>
>>    cout << "BEFORE:" << endl;
>>    cout << "pt_ll: (" << pt_ll->x << ", " << pt_ll->y << ")" << endl;
>>    cout << "pt_ur: (" << pt_ur->x << ", " << pt_ur->y << ")" << endl;
>>    cout << endl;
>>
>>    // *** Just to confirm, that the mapObj is working as expected, I 
>> test this statement and indeed,
>>    // *** the map is recentered correctly....
>>    msMapSetExtent( map, pt_ll->x, pt_ll->y, pt_ur->x, pt_ur->y );
>>
>>
>>    // *** Create projectionObj pointers to convert between geographic 
>> coordinates
>>    // *** into the Transverse Mercator projection required for USGS 
>> quads.
>>    // *** projectionObj_new is defined elsewhere and functions 
>> properly...as the
>>    // *** transformed coordinates are correct.
>>    projectionObj *projIn = projectionObj_new( "+init=epsg:4326" );
>>
>>    string s_projOut = "+proj=tmerc +lat_0=0.0";
>>    s_projOut.append( " +lon_0=" );
>>    s_projOut.append( doubleToString( (double)((pt_ll->x + pt_ur->x) / 
>> 2.0) ) );
>>    s_projOut.append( " +k=0.9996 +x_0=0.0 +y_0=0.0 +ellps=GRS80" );
>>    projectionObj *projOut = projectionObj_new( _strdup( 
>> s_projOut.c_str() ) );
>>
>>    cout << "Output Projection: " << msGetProjectionString( projOut ) 
>> << endl;
>>
>>
>>    // *** Reproject the input points.  One point for the lower-left 
>> coordinate
>>    // *** and one for the upper-right corner.
>>    msProjectPoint( projIn, projOut, pt_ll );
>>    msProjectPoint( projIn, projOut, pt_ur );
>>
>>    // *** These points are correct! No problem here....
>>    cout << "AFTER: " << endl;
>>    cout << "p_min: (" << pt_ll->x << ", " << pt_ll->y << ")" << endl;
>>    cout << "p_max: (" << pt_ur->x << ", " << pt_ur->y << ")" << endl;
>>    cout << endl;
>>
>>    // *** This is where things fall apart.  Taking a page from the 
>> php_mapscript.c and
>>    // *** mapscript_i.c sources, I init the projectionObj pointers, 
>> run msLoadProjectionString,
>>    // *** and recompure the extent of the 
>> map....msLoadProjectionString by itself doesn't work either.
>>    int nStatus = 0;
>>    int nUnits = MS_METERS;
>>    projectionObj in;
>>    projectionObj out;
>>    rectObj sRect;
>>    int bSetNewExtents = 0;
>>    int bSetUnitsAndExtents = 1;
>>
>>    msInitProjection( &in );
>>    in = map->projection;
>>    msInitProjection( &out );
>>
>>    msLoadProjectionString( &(out), msGetProjectionString( projOut ) );
>>    sRect = map->extent;
>>
>>    if (in.proj != NULL && out.proj != NULL)
>>    {
>>        if (msProjectionsDiffer( &in, &out ) )
>>        {
>>            if (msProjectRect( &in, &out, &sRect) == 0)
>>                bSetNewExtents = 1;
>>        }
>>    }
>>
>>    msLoadProjectionString( &map->projection, msGetProjectionString( 
>> projOut ) );
>>
>>    nUnits = GetMapserverUnitUsingProj( &map->projection );
>>    map->units = (MS_UNITS)nUnits;
>>    if (bSetNewExtents == 1)
>>    {
>>        map->extent = sRect;
>>        map->cellsize = msAdjustExtent( &map->extent, map->width, 
>> map->height );
>>        msCalculateScale( map->extent, map->units, map->width, 
>> map->height, map->resolution, &map->scale );
>>    }
>>
>>
>>    // *** Compute the size of the output map in meters.  This is 
>> later used to
>>    // *** determine the output size of the map in pixels.
>>    double width_m = pt_ur->x - pt_ll->x;
>>    double height_m = pt_ur->x - pt_ll->y;
>>
>>    double width = 500.0;
>>    double height = ((height_m / width_m) * width);
>>    cout << "EARTH SIZE [Width, Height] m: " << ((double)width_m) << 
>> ", " << ((double)height_m) << endl;
>>    cout << endl;
>>
>>
>>    // *** Set the size of the output map.
>>    msMapSetSize( map, (int)width, (int)height );
>>    cout << "MAP SIZE [Width, Height]: " << map->width << ", " << 
>> map->height << endl;
>>
>>
>>    // *** Set the extent of the map as per the newly projected 
>> coordinates
>>    // *** computed above.
>>
>>    // *** This call is where things go wrong.  Apparently the 
>> msLoadProjectionString is not
>>    // *** working properly, because if I use the original coordinates 
>> supplied as parameters
>>    // *** the map moves to the correct location -- not the projected 
>> location.
>>    msMapSetExtent( map, pt_ll->x, pt_ll->y, pt_ur->x, pt_ur->y );
>>
>>
>>    image = msDrawMap( map );
>>    msSaveImage( map, image, "C:\\simple.png");
>>
>>    pointObj_destroy( pt_ll );
>>    pointObj_destroy( pt_ur );
>>    msFreeImage( image );
>>    msFreeMap( map );
>>    msCleanup();
>>    cout << "Complete!" << endl;
>> }
>> // ------------------------------------------------------------------
>>
>> Please! I'm looking for some guidance here as it doesn't seem too 
>> difficult - and the same order and function calls work in PHP/MapScript!
>>
>
>



More information about the mapserver-dev mailing list