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

Daniel Morissette dmorissette at MAPGEARS.COM
Thu Jun 21 11:16:52 EDT 2007


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!
> 


-- 
Daniel Morissette
http://www.mapgears.com/



More information about the mapserver-dev mailing list